@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,1270 @@
1
+ /**
2
+ * Advanced Security Vulnerability Detector
3
+ *
4
+ * Detects advanced AD vulnerabilities including ADCS, LAPS, Shadow Credentials, RBCD, DCSync, etc.
5
+ * Story 1.7: AD Vulnerability Detection Engine
6
+ * Story 1.1: SMB & LDAP Signing Detection
7
+ *
8
+ * Vulnerabilities detected (36):
9
+ * CRITICAL (5):
10
+ * - SHADOW_CREDENTIALS
11
+ * - RBCD_ABUSE
12
+ * - EXCHANGE_PRIV_ESC_PATH - Phase 4
13
+ * - LDAP_SIGNING_DISABLED - LDAP signing not required (Story 1.1)
14
+ * - SMB_SIGNING_DISABLED - SMB signing not required (Story 1.1)
15
+ *
16
+ * HIGH (11):
17
+ * - ESC1_VULNERABLE_TEMPLATE
18
+ * - ESC2_ANY_PURPOSE
19
+ * - ESC3_ENROLLMENT_AGENT
20
+ * - ESC4_VULNERABLE_TEMPLATE_ACL
21
+ * - ESC6_EDITF_ATTRIBUTESUBJECTALTNAME2
22
+ * - LAPS_PASSWORD_READABLE
23
+ * - REPLICATION_RIGHTS
24
+ * - DCSYNC_CAPABLE
25
+ * - MACHINE_ACCOUNT_QUOTA_HIGH - Quota > 10 (intentionally increased)
26
+ * - LDAP_CHANNEL_BINDING_DISABLED - LDAP channel binding not required
27
+ * - SMB_V1_ENABLED - SMBv1 protocol enabled
28
+ * - ADMIN_SD_HOLDER_MODIFIED - Phase 4
29
+ *
30
+ * MEDIUM (17):
31
+ * - ESC8_HTTP_ENROLLMENT
32
+ * - LAPS_NOT_DEPLOYED
33
+ * - LAPS_LEGACY_ATTRIBUTE
34
+ * - DUPLICATE_SPN
35
+ * - WEAK_PASSWORD_POLICY
36
+ * - WEAK_KERBEROS_POLICY
37
+ * - MACHINE_ACCOUNT_QUOTA_ABUSE
38
+ * - DELEGATION_PRIVILEGE
39
+ * - ADCS_WEAK_PERMISSIONS
40
+ * - DANGEROUS_LOGON_SCRIPTS
41
+ * - FOREIGN_SECURITY_PRINCIPALS
42
+ * - NTLM_RELAY_OPPORTUNITY
43
+ * - RECYCLE_BIN_DISABLED - AD Recycle Bin not enabled
44
+ * - ANONYMOUS_LDAP_ACCESS - Anonymous LDAP bind allowed
45
+ * - AUDIT_POLICY_WEAK - Audit policy incomplete
46
+ * - POWERSHELL_LOGGING_DISABLED - PowerShell logging not configured
47
+ * - DS_HEURISTICS_MODIFIED - Phase 4
48
+ *
49
+ * LOW (2):
50
+ * - LAPS_PASSWORD_SET
51
+ * - LAPS_PASSWORD_LEAKED
52
+ */
53
+
54
+ import { ADUser, ADComputer, ADDomain } from '../../../../types/ad.types';
55
+ import { Finding } from '../../../../types/finding.types';
56
+ import { toAffectedUserEntities } from '../../../../utils/entity-converter';
57
+
58
+ /**
59
+ * Check for Shadow Credentials attack (msDS-KeyCredentialLink)
60
+ */
61
+ export function detectShadowCredentials(users: ADUser[], includeDetails: boolean): Finding {
62
+ const affected = users.filter((u) => {
63
+ const keyLink = (u as any)['msDS-KeyCredentialLink'];
64
+ // Check that attribute exists, is not null, not empty string, and not empty array
65
+ return keyLink && (Array.isArray(keyLink) ? keyLink.length > 0 : keyLink !== '');
66
+ });
67
+
68
+ return {
69
+ type: 'SHADOW_CREDENTIALS',
70
+ severity: 'critical',
71
+ category: 'advanced',
72
+ title: 'Shadow Credentials',
73
+ description: 'msDS-KeyCredentialLink attribute configured. Allows Kerberos authentication bypass by adding arbitrary public keys.',
74
+ count: affected.length,
75
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Check for RBCD abuse (Resource-Based Constrained Delegation)
81
+ */
82
+ export function detectRbcdAbuse(users: ADUser[], includeDetails: boolean): Finding {
83
+ const affected = users.filter((u) => {
84
+ const rbcdAttr = (u as any)['msDS-AllowedToActOnBehalfOfOtherIdentity'];
85
+ // Check that attribute exists, is not null, not empty string, and not empty array
86
+ return rbcdAttr && (Array.isArray(rbcdAttr) ? rbcdAttr.length > 0 : rbcdAttr !== '');
87
+ });
88
+
89
+ return {
90
+ type: 'RBCD_ABUSE',
91
+ severity: 'critical',
92
+ category: 'advanced',
93
+ title: 'RBCD Abuse',
94
+ description: 'msDS-AllowedToActOnBehalfOfOtherIdentity configured. Enables privilege escalation via resource-based delegation.',
95
+ count: affected.length,
96
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Check for ESC1 vulnerable certificate template
102
+ */
103
+ export function detectEsc1VulnerableTemplate(templates: any[], includeDetails: boolean): Finding {
104
+ const affected = templates.filter((t) => {
105
+ const hasClientAuth = t.pKIExtendedKeyUsage?.includes('1.3.6.1.5.5.7.3.2');
106
+ const enrolleeSuppliesSubject = t['msPKI-Certificate-Name-Flag'] && (t['msPKI-Certificate-Name-Flag'] & 0x1) !== 0;
107
+ return hasClientAuth && enrolleeSuppliesSubject;
108
+ });
109
+
110
+ return {
111
+ type: 'ESC1_VULNERABLE_TEMPLATE',
112
+ severity: 'high',
113
+ category: 'advanced',
114
+ title: 'ESC1 Vulnerable Template',
115
+ description: 'ADCS template with client auth + enrollee supplies subject. Enables domain compromise by obtaining cert for any user.',
116
+ count: affected.length,
117
+ affectedEntities: includeDetails ? affected.map((t) => t.dn) : undefined,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Check for ESC2 Any Purpose EKU
123
+ */
124
+ export function detectEsc2AnyPurpose(templates: any[], includeDetails: boolean): Finding {
125
+ const affected = templates.filter((t) => {
126
+ const hasAnyPurpose = t.pKIExtendedKeyUsage?.includes('2.5.29.37.0');
127
+ const isEmpty = !t.pKIExtendedKeyUsage || t.pKIExtendedKeyUsage.length === 0;
128
+ return hasAnyPurpose || isEmpty;
129
+ });
130
+
131
+ return {
132
+ type: 'ESC2_ANY_PURPOSE',
133
+ severity: 'high',
134
+ category: 'advanced',
135
+ title: 'ESC2 Any Purpose',
136
+ description: 'ADCS template with Any Purpose EKU or no usage restriction. Certificate can be used for domain authentication.',
137
+ count: affected.length,
138
+ affectedEntities: includeDetails ? affected.map((t) => t.dn) : undefined,
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Check for ESC3 enrollment agent
144
+ */
145
+ export function detectEsc3EnrollmentAgent(templates: any[], includeDetails: boolean): Finding {
146
+ const affected = templates.filter((t) => {
147
+ return t.pKIExtendedKeyUsage?.includes('1.3.6.1.4.1.311.20.2.1');
148
+ });
149
+
150
+ return {
151
+ type: 'ESC3_ENROLLMENT_AGENT',
152
+ severity: 'high',
153
+ category: 'advanced',
154
+ title: 'ESC3 Enrollment Agent',
155
+ description: 'ADCS template with enrollment agent EKU. Can request certificates on behalf of other users.',
156
+ count: affected.length,
157
+ affectedEntities: includeDetails ? affected.map((t) => t.dn) : undefined,
158
+ };
159
+ }
160
+
161
+ /**
162
+ * Check for ESC4 vulnerable template ACL
163
+ */
164
+ export function detectEsc4VulnerableTemplateAcl(templates: any[], includeDetails: boolean): Finding {
165
+ const affected = templates.filter((t) => {
166
+ return t.hasWeakAcl; // This would be populated by ACL analysis
167
+ });
168
+
169
+ return {
170
+ type: 'ESC4_VULNERABLE_TEMPLATE_ACL',
171
+ severity: 'high',
172
+ category: 'advanced',
173
+ title: 'ESC4 Vulnerable Template ACL',
174
+ description: 'Certificate template with weak ACLs. Can modify template to make it vulnerable to ESC1/ESC2.',
175
+ count: affected.length,
176
+ affectedEntities: includeDetails ? affected.map((t) => t.dn) : undefined,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Check for ESC6 EDITF_ATTRIBUTESUBJECTALTNAME2
182
+ */
183
+ export function detectEsc6EditfAttributeSubjectAltName2(cas: any[], includeDetails: boolean): Finding {
184
+ const affected = cas.filter((ca) => {
185
+ return ca.flags && (ca.flags & 0x40000) !== 0; // EDITF_ATTRIBUTESUBJECTALTNAME2
186
+ });
187
+
188
+ return {
189
+ type: 'ESC6_EDITF_ATTRIBUTESUBJECTALTNAME2',
190
+ severity: 'high',
191
+ category: 'advanced',
192
+ title: 'ESC6 EDITF Flag',
193
+ description: 'ADCS CA with EDITF_ATTRIBUTESUBJECTALTNAME2 flag. Allows specifying arbitrary SAN in certificate requests.',
194
+ count: affected.length,
195
+ affectedEntities: includeDetails ? affected.map((ca) => ca.dn) : undefined,
196
+ };
197
+ }
198
+
199
+ /**
200
+ * Check for LAPS password readable by non-admins
201
+ */
202
+ export function detectLapsPasswordReadable(computers: ADComputer[], includeDetails: boolean): Finding {
203
+ const affected = computers.filter((c) => {
204
+ return (c as any).lapsPasswordReadableByNonAdmins; // This would be populated by ACL analysis
205
+ });
206
+
207
+ return {
208
+ type: 'LAPS_PASSWORD_READABLE',
209
+ severity: 'high',
210
+ category: 'advanced',
211
+ title: 'LAPS Password Readable',
212
+ description: 'Non-admin users can read LAPS password attributes. Exposure of local admin passwords.',
213
+ count: affected.length,
214
+ affectedEntities: includeDetails ? affected.map((c) => c.dn) : undefined,
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Check for accounts with replication rights (potential DCSync)
220
+ */
221
+ export function detectReplicationRights(users: ADUser[], includeDetails: boolean): Finding {
222
+ const affected = users.filter((u) => {
223
+ // adminCount=1 but not in standard admin groups
224
+ if (u.adminCount !== 1) return false;
225
+ if (!u.memberOf) return true; // Has adminCount but no groups
226
+
227
+ const isInStandardAdminGroups = u.memberOf.some((dn) => {
228
+ return (
229
+ dn.includes('CN=Domain Admins') ||
230
+ dn.includes('CN=Enterprise Admins') ||
231
+ dn.includes('CN=Administrators')
232
+ );
233
+ });
234
+
235
+ return !isInStandardAdminGroups;
236
+ });
237
+
238
+ return {
239
+ type: 'REPLICATION_RIGHTS',
240
+ severity: 'high',
241
+ category: 'advanced',
242
+ title: 'Replication Rights',
243
+ description: 'Account with adminCount=1 outside standard admin groups. May have replication rights (DCSync).',
244
+ count: affected.length,
245
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
246
+ };
247
+ }
248
+
249
+ /**
250
+ * Check for DCSync capable accounts
251
+ */
252
+ export function detectDcsyncCapable(users: ADUser[], includeDetails: boolean): Finding {
253
+ const affected = users.filter((u) => {
254
+ return (u as any).hasDcsyncRights; // This would be populated by ACL analysis
255
+ });
256
+
257
+ return {
258
+ type: 'DCSYNC_CAPABLE',
259
+ severity: 'high',
260
+ category: 'advanced',
261
+ title: 'DCSync Capable',
262
+ description: 'Account with DS-Replication-Get-Changes and DS-Replication-Get-Changes-All rights. Can extract all password hashes.',
263
+ count: affected.length,
264
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Check for ESC8 HTTP enrollment
270
+ */
271
+ export function detectEsc8HttpEnrollment(cas: any[], includeDetails: boolean): Finding {
272
+ const affected = cas.filter((ca) => {
273
+ return ca.webEnrollment && ca.webEnrollment.protocol === 'http';
274
+ });
275
+
276
+ return {
277
+ type: 'ESC8_HTTP_ENROLLMENT',
278
+ severity: 'medium',
279
+ category: 'advanced',
280
+ title: 'ESC8 HTTP Enrollment',
281
+ description: 'ADCS web enrollment via HTTP. Enables NTLM relay attacks against certificate enrollment.',
282
+ count: affected.length,
283
+ affectedEntities: includeDetails ? affected.map((ca) => ca.dn) : undefined,
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Check for LAPS not deployed
289
+ */
290
+ export function detectLapsNotDeployed(computers: ADComputer[], includeDetails: boolean): Finding {
291
+ const affected = computers.filter((c) => {
292
+ return !('ms-Mcs-AdmPwd' in c) && !('msLAPS-Password' in c);
293
+ });
294
+
295
+ return {
296
+ type: 'LAPS_NOT_DEPLOYED',
297
+ severity: 'medium',
298
+ category: 'advanced',
299
+ title: 'LAPS Not Deployed',
300
+ description: 'LAPS not deployed on domain computers. Shared/static local admin passwords across workstations.',
301
+ count: affected.length,
302
+ affectedEntities: includeDetails ? affected.map((c) => c.dn) : undefined,
303
+ };
304
+ }
305
+
306
+ /**
307
+ * Check for legacy LAPS attribute
308
+ */
309
+ export function detectLapsLegacyAttribute(computers: ADComputer[], includeDetails: boolean): Finding {
310
+ const affected = computers.filter((c) => {
311
+ return 'ms-Mcs-AdmPwd' in c && !('msLAPS-Password' in c);
312
+ });
313
+
314
+ return {
315
+ type: 'LAPS_LEGACY_ATTRIBUTE',
316
+ severity: 'medium',
317
+ category: 'advanced',
318
+ title: 'LAPS Legacy Attribute',
319
+ description: 'Legacy LAPS attribute used instead of Windows LAPS. Less secure implementation.',
320
+ count: affected.length,
321
+ affectedEntities: includeDetails ? affected.map((c) => c.dn) : undefined,
322
+ };
323
+ }
324
+
325
+ /**
326
+ * Check for duplicate SPNs
327
+ */
328
+ export function detectDuplicateSpn(users: ADUser[], includeDetails: boolean): Finding {
329
+ const spnMap = new Map<string, string[]>();
330
+
331
+ // Build SPN to user DN mapping
332
+ users.forEach((u) => {
333
+ const spns = (u as any).servicePrincipalName;
334
+ if (spns && Array.isArray(spns)) {
335
+ spns.forEach((spn: string) => {
336
+ if (!spnMap.has(spn)) {
337
+ spnMap.set(spn, []);
338
+ }
339
+ spnMap.get(spn)!.push(u.dn);
340
+ });
341
+ }
342
+ });
343
+
344
+ // Find duplicate SPNs
345
+ const affected: string[] = [];
346
+ spnMap.forEach((dns, _spn) => {
347
+ if (dns.length > 1) {
348
+ affected.push(...dns);
349
+ }
350
+ });
351
+
352
+ return {
353
+ type: 'DUPLICATE_SPN',
354
+ severity: 'medium',
355
+ category: 'advanced',
356
+ title: 'Duplicate SPN',
357
+ description: 'Service Principal Name registered multiple times. Can cause Kerberos authentication failures.',
358
+ count: affected.length,
359
+ affectedEntities: includeDetails ? affected : undefined,
360
+ };
361
+ }
362
+
363
+ /**
364
+ * Check for weak password policy
365
+ */
366
+ export function detectWeakPasswordPolicy(domain: ADDomain | null, includeDetails: boolean): Finding {
367
+ if (!domain) {
368
+ return {
369
+ type: 'WEAK_PASSWORD_POLICY',
370
+ severity: 'medium',
371
+ category: 'advanced',
372
+ title: 'Weak Password Policy',
373
+ description: 'Unable to check domain password policy.',
374
+ count: 0,
375
+ };
376
+ }
377
+
378
+ const minPwdLength = (domain as any).minPwdLength || 0;
379
+ const maxPwdAge = (domain as any).maxPwdAge || 0;
380
+ const pwdHistoryLength = (domain as any).pwdHistoryLength || 0;
381
+
382
+ const isWeak = minPwdLength < 14 || maxPwdAge > 90 || pwdHistoryLength < 24;
383
+
384
+ return {
385
+ type: 'WEAK_PASSWORD_POLICY',
386
+ severity: 'medium',
387
+ category: 'advanced',
388
+ title: 'Weak Password Policy',
389
+ description: 'Domain password policy below minimum standards. Enables easier password cracking.',
390
+ count: isWeak ? 1 : 0,
391
+ affectedEntities: includeDetails && isWeak ? [domain.dn] : undefined,
392
+ details: isWeak
393
+ ? {
394
+ minPwdLength,
395
+ maxPwdAge,
396
+ pwdHistoryLength,
397
+ }
398
+ : undefined,
399
+ };
400
+ }
401
+
402
+ /**
403
+ * Check for weak Kerberos policy
404
+ */
405
+ export function detectWeakKerberosPolicy(domain: ADDomain | null, includeDetails: boolean): Finding {
406
+ if (!domain) {
407
+ return {
408
+ type: 'WEAK_KERBEROS_POLICY',
409
+ severity: 'medium',
410
+ category: 'advanced',
411
+ title: 'Weak Kerberos Policy',
412
+ description: 'Unable to check Kerberos policy.',
413
+ count: 0,
414
+ };
415
+ }
416
+
417
+ const maxTicketAge = (domain as any).maxTicketAge || 0;
418
+ const maxRenewAge = (domain as any).maxRenewAge || 0;
419
+
420
+ const isWeak = maxTicketAge > 10 || maxRenewAge > 7;
421
+
422
+ return {
423
+ type: 'WEAK_KERBEROS_POLICY',
424
+ severity: 'medium',
425
+ category: 'advanced',
426
+ title: 'Weak Kerberos Policy',
427
+ description: 'Kerberos ticket lifetimes exceed recommended values. Longer window for ticket-based attacks.',
428
+ count: isWeak ? 1 : 0,
429
+ affectedEntities: includeDetails && isWeak ? [domain.dn] : undefined,
430
+ details: isWeak
431
+ ? {
432
+ maxTicketAge,
433
+ maxRenewAge,
434
+ }
435
+ : undefined,
436
+ };
437
+ }
438
+
439
+ /**
440
+ * Check for machine account quota abuse
441
+ */
442
+ export function detectMachineAccountQuotaAbuse(domain: ADDomain | null, includeDetails: boolean): Finding {
443
+ if (!domain) {
444
+ return {
445
+ type: 'MACHINE_ACCOUNT_QUOTA_ABUSE',
446
+ severity: 'medium',
447
+ category: 'advanced',
448
+ title: 'Machine Account Quota Abuse',
449
+ description: 'Unable to check machine account quota.',
450
+ count: 0,
451
+ };
452
+ }
453
+
454
+ const quota = (domain as any)['ms-DS-MachineAccountQuota'];
455
+ const isVulnerable = typeof quota === 'number' && quota > 0;
456
+
457
+ return {
458
+ type: 'MACHINE_ACCOUNT_QUOTA_ABUSE',
459
+ severity: 'medium',
460
+ category: 'advanced',
461
+ title: 'Machine Account Quota Abuse',
462
+ description: 'ms-DS-MachineAccountQuota > 0. Non-admin users can join computers to domain (potential Kerberos attacks).',
463
+ count: isVulnerable ? 1 : 0,
464
+ affectedEntities: includeDetails && isVulnerable ? [domain.dn] : undefined,
465
+ details: isVulnerable
466
+ ? {
467
+ quota,
468
+ }
469
+ : undefined,
470
+ };
471
+ }
472
+
473
+ /**
474
+ * Check for machine account quota set higher than default
475
+ * Default is 10 - values > 10 indicate intentional increase
476
+ */
477
+ export function detectMachineAccountQuotaHigh(domain: ADDomain | null, includeDetails: boolean): Finding {
478
+ if (!domain) {
479
+ return {
480
+ type: 'MACHINE_ACCOUNT_QUOTA_HIGH',
481
+ severity: 'high',
482
+ category: 'advanced',
483
+ title: 'Machine Account Quota Elevated',
484
+ description: 'Unable to check machine account quota.',
485
+ count: 0,
486
+ };
487
+ }
488
+
489
+ const quota = (domain as any)['ms-DS-MachineAccountQuota'];
490
+ const DEFAULT_QUOTA = 10;
491
+ const isElevated = typeof quota === 'number' && quota > DEFAULT_QUOTA;
492
+
493
+ return {
494
+ type: 'MACHINE_ACCOUNT_QUOTA_HIGH',
495
+ severity: 'high',
496
+ category: 'advanced',
497
+ title: 'Machine Account Quota Elevated Above Default',
498
+ description:
499
+ 'ms-DS-MachineAccountQuota is higher than the default (10). Someone intentionally increased this value, allowing non-admin users to join more computers to the domain.',
500
+ count: isElevated ? 1 : 0,
501
+ affectedEntities: includeDetails && isElevated ? [domain.dn] : undefined,
502
+ details: isElevated
503
+ ? {
504
+ currentQuota: quota,
505
+ defaultQuota: DEFAULT_QUOTA,
506
+ recommendation: 'Set ms-DS-MachineAccountQuota to 0 to prevent non-admin domain joins.',
507
+ }
508
+ : undefined,
509
+ };
510
+ }
511
+
512
+ /**
513
+ * Check for delegation privilege
514
+ */
515
+ export function detectDelegationPrivilege(users: ADUser[], includeDetails: boolean): Finding {
516
+ const affected = users.filter((u) => {
517
+ return (u as any).hasSeEnableDelegationPrivilege;
518
+ });
519
+
520
+ return {
521
+ type: 'DELEGATION_PRIVILEGE',
522
+ severity: 'medium',
523
+ category: 'advanced',
524
+ title: 'Delegation Privilege',
525
+ description: 'Account has SeEnableDelegationPrivilege. Can enable delegation on user/computer accounts.',
526
+ count: affected.length,
527
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Check for foreign security principals
533
+ */
534
+ export function detectForeignSecurityPrincipals(fsps: any[], includeDetails: boolean): Finding {
535
+ return {
536
+ type: 'FOREIGN_SECURITY_PRINCIPALS',
537
+ severity: 'medium',
538
+ category: 'advanced',
539
+ title: 'Foreign Security Principals',
540
+ description: 'Foreign security principals from external forests. Potential for cross-forest privilege escalation.',
541
+ count: fsps.length,
542
+ affectedEntities: includeDetails ? fsps.map((fsp) => fsp.dn) : undefined,
543
+ };
544
+ }
545
+
546
+ /**
547
+ * Check for NTLM relay opportunity
548
+ */
549
+ export function detectNtlmRelayOpportunity(domain: ADDomain | null, includeDetails: boolean): Finding {
550
+ if (!domain) {
551
+ return {
552
+ type: 'NTLM_RELAY_OPPORTUNITY',
553
+ severity: 'medium',
554
+ category: 'advanced',
555
+ title: 'NTLM Relay Opportunity',
556
+ description: 'Unable to check LDAP signing configuration.',
557
+ count: 0,
558
+ };
559
+ }
560
+
561
+ const ldapSigningRequired = (domain as any).ldapSigningRequired;
562
+ const channelBindingRequired = (domain as any).channelBindingRequired;
563
+
564
+ const isVulnerable = !ldapSigningRequired || !channelBindingRequired;
565
+
566
+ return {
567
+ type: 'NTLM_RELAY_OPPORTUNITY',
568
+ severity: 'medium',
569
+ category: 'advanced',
570
+ title: 'NTLM Relay Opportunity',
571
+ description: 'LDAP signing or channel binding not enforced. Enables NTLM relay attacks.',
572
+ count: isVulnerable ? 1 : 0,
573
+ affectedEntities: includeDetails && isVulnerable ? [domain.dn] : undefined,
574
+ };
575
+ }
576
+
577
+ /**
578
+ * Check for LAPS password set (informational)
579
+ */
580
+ export function detectLapsPasswordSet(computers: ADComputer[], includeDetails: boolean): Finding {
581
+ const affected = computers.filter((c) => {
582
+ return 'ms-Mcs-AdmPwd' in c || 'msLAPS-Password' in c;
583
+ });
584
+
585
+ return {
586
+ type: 'LAPS_PASSWORD_SET',
587
+ severity: 'low',
588
+ category: 'advanced',
589
+ title: 'LAPS Password Set',
590
+ description: 'LAPS password successfully managed. Informational - indicates proper LAPS deployment.',
591
+ count: affected.length,
592
+ affectedEntities: includeDetails ? affected.map((c) => c.dn) : undefined,
593
+ };
594
+ }
595
+
596
+ /**
597
+ * Check for LAPS password leaked
598
+ */
599
+ export function detectLapsPasswordLeaked(computers: ADComputer[], includeDetails: boolean): Finding {
600
+ const affected = computers.filter((c) => {
601
+ return (c as any).lapsPasswordExcessiveReaders; // This would be populated by ACL analysis
602
+ });
603
+
604
+ return {
605
+ type: 'LAPS_PASSWORD_LEAKED',
606
+ severity: 'low',
607
+ category: 'advanced',
608
+ title: 'LAPS Password Leaked',
609
+ description: 'LAPS password visible to too many users. Reduces effectiveness of LAPS.',
610
+ count: affected.length,
611
+ affectedEntities: includeDetails ? affected.map((c) => c.dn) : undefined,
612
+ };
613
+ }
614
+
615
+ /**
616
+ * Check for ADCS weak permissions
617
+ */
618
+ export function detectAdcsWeakPermissions(templates: any[], includeDetails: boolean): Finding {
619
+ const affected = templates.filter((t) => {
620
+ // Check if template has weak ACLs allowing enrollment by non-admins
621
+ return t.hasWeakEnrollmentAcl || t.hasGenericAllPermission;
622
+ });
623
+
624
+ return {
625
+ type: 'ADCS_WEAK_PERMISSIONS',
626
+ severity: 'medium',
627
+ category: 'advanced',
628
+ title: 'ADCS Weak Permissions',
629
+ description: 'Weak permissions on ADCS objects or certificate templates allow unauthorized enrollment.',
630
+ count: affected.length,
631
+ affectedEntities: includeDetails ? affected.map((t) => t.dn || t.name) : undefined,
632
+ };
633
+ }
634
+
635
+ /**
636
+ * Check for dangerous logon scripts with weak ACLs
637
+ */
638
+ export function detectDangerousLogonScripts(users: ADUser[], includeDetails: boolean): Finding {
639
+ const affected = users.filter((u) => {
640
+ const scriptPath = (u as any).scriptPath;
641
+ // Check if user has logon script configured
642
+ // In real implementation, would check ACLs on script file/share
643
+ return scriptPath && (scriptPath.includes('\\\\') || scriptPath.startsWith('//'));
644
+ });
645
+
646
+ return {
647
+ type: 'DANGEROUS_LOGON_SCRIPTS',
648
+ severity: 'medium',
649
+ category: 'advanced',
650
+ title: 'Dangerous Logon Scripts',
651
+ description: 'Logon scripts with weak ACLs can be modified by attackers for code execution on user login.',
652
+ count: affected.length,
653
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
654
+ };
655
+ }
656
+
657
+ // ==================== PHASE 1B DETECTORS ====================
658
+
659
+ /**
660
+ * Check if anonymous LDAP access is allowed
661
+ * This is tested during the audit via separate anonymous bind attempt
662
+ */
663
+ export function detectAnonymousLdapAccess(
664
+ anonymousAccessAllowed: boolean,
665
+ domain: ADDomain | null,
666
+ includeDetails: boolean
667
+ ): Finding {
668
+ return {
669
+ type: 'ANONYMOUS_LDAP_ACCESS',
670
+ severity: 'medium',
671
+ category: 'advanced',
672
+ title: 'Anonymous LDAP Access Allowed',
673
+ description:
674
+ 'LDAP server accepts anonymous binds. Attackers can enumerate AD objects (users, groups, computers) without valid credentials.',
675
+ count: anonymousAccessAllowed ? 1 : 0,
676
+ affectedEntities: includeDetails && anonymousAccessAllowed && domain ? [domain.dn] : undefined,
677
+ details: anonymousAccessAllowed
678
+ ? {
679
+ recommendation:
680
+ 'Configure "Network security: LDAP client signing requirements" and restrict anonymous access via dsHeuristics.',
681
+ currentStatus: 'Anonymous bind allowed',
682
+ }
683
+ : undefined,
684
+ };
685
+ }
686
+
687
+ /**
688
+ * Check if AD Recycle Bin is disabled
689
+ * Recycle Bin allows recovery of deleted objects
690
+ */
691
+ export function detectRecycleBinDisabled(domain: ADDomain | null, includeDetails: boolean): Finding {
692
+ if (!domain) {
693
+ return {
694
+ type: 'RECYCLE_BIN_DISABLED',
695
+ severity: 'medium',
696
+ category: 'advanced',
697
+ title: 'AD Recycle Bin Status Unknown',
698
+ description: 'Unable to determine AD Recycle Bin status.',
699
+ count: 0,
700
+ };
701
+ }
702
+
703
+ const recycleBinEnabled = (domain as any).recycleBinEnabled === true;
704
+
705
+ return {
706
+ type: 'RECYCLE_BIN_DISABLED',
707
+ severity: 'medium',
708
+ category: 'advanced',
709
+ title: 'AD Recycle Bin Not Enabled',
710
+ description:
711
+ 'Active Directory Recycle Bin is not enabled. Deleted objects cannot be easily recovered, which complicates incident response and may lead to permanent data loss.',
712
+ count: recycleBinEnabled ? 0 : 1,
713
+ affectedEntities: includeDetails && !recycleBinEnabled ? [domain.dn] : undefined,
714
+ details: !recycleBinEnabled
715
+ ? {
716
+ recommendation:
717
+ 'Enable AD Recycle Bin feature. Note: This requires forest functional level 2008 R2 or higher and is irreversible.',
718
+ currentStatus: 'Disabled',
719
+ }
720
+ : undefined,
721
+ };
722
+ }
723
+
724
+ /**
725
+ * GPO Security Settings from SYSVOL
726
+ */
727
+ export interface GpoSecuritySettings {
728
+ /** LDAP server signing requirement: 0=none, 1=negotiate, 2=require */
729
+ ldapServerIntegrity?: number;
730
+ /** LDAP channel binding: 0=never, 1=when supported, 2=always */
731
+ ldapChannelBinding?: number;
732
+ /** SMBv1 server enabled */
733
+ smbv1ServerEnabled?: boolean;
734
+ /** SMBv1 client enabled */
735
+ smbv1ClientEnabled?: boolean;
736
+ /** SMB Server signing required (RequireSecuritySignature) */
737
+ smbSigningRequired?: boolean;
738
+ /** SMB Client signing required */
739
+ smbClientSigningRequired?: boolean;
740
+ /** Audit policies configured */
741
+ auditPolicies?: {
742
+ category: string;
743
+ subcategory?: string;
744
+ success: boolean;
745
+ failure: boolean;
746
+ }[];
747
+ /** PowerShell logging settings */
748
+ powershellLogging?: {
749
+ moduleLogging: boolean;
750
+ scriptBlockLogging: boolean;
751
+ transcription: boolean;
752
+ };
753
+ }
754
+
755
+ /**
756
+ * Check if LDAP signing is disabled
757
+ * Requires GPO settings from SYSVOL
758
+ */
759
+ export function detectLdapSigningDisabled(
760
+ gpoSettings: GpoSecuritySettings | null,
761
+ domain: ADDomain | null,
762
+ includeDetails: boolean
763
+ ): Finding {
764
+ // If we have GPO settings, use them
765
+ if (gpoSettings && gpoSettings.ldapServerIntegrity !== undefined) {
766
+ const signingDisabled = gpoSettings.ldapServerIntegrity === 0;
767
+
768
+ return {
769
+ type: 'LDAP_SIGNING_DISABLED',
770
+ severity: 'critical',
771
+ category: 'advanced',
772
+ title: 'LDAP Signing Not Required',
773
+ description:
774
+ 'LDAP server signing is not required. This allows NTLM relay attacks and man-in-the-middle attacks against LDAP connections.',
775
+ count: signingDisabled ? 1 : 0,
776
+ affectedEntities: includeDetails && signingDisabled && domain ? [domain.dn] : undefined,
777
+ details: signingDisabled
778
+ ? {
779
+ recommendation: 'Configure "Domain controller: LDAP server signing requirements" to "Require signing".',
780
+ currentSetting: gpoSettings.ldapServerIntegrity,
781
+ requiredSetting: 2,
782
+ }
783
+ : undefined,
784
+ };
785
+ }
786
+
787
+ // If GPO settings not available, assume vulnerable (Windows defaults don't require LDAP signing)
788
+ return {
789
+ type: 'LDAP_SIGNING_DISABLED',
790
+ severity: 'critical',
791
+ category: 'advanced',
792
+ title: 'LDAP Signing Not Configured in GPO',
793
+ description:
794
+ 'LDAP signing is not configured via Group Policy. Windows defaults do not require LDAP signing, making this environment vulnerable to LDAP relay attacks.',
795
+ count: 1,
796
+ affectedEntities: includeDetails && domain ? [domain.dn] : undefined,
797
+ details: {
798
+ recommendation:
799
+ 'Configure "Domain controller: LDAP server signing requirements" to "Require signing" via Group Policy.',
800
+ note: 'No GPO security template found. Windows defaults do not require LDAP signing.',
801
+ },
802
+ };
803
+ }
804
+
805
+ /**
806
+ * Check if LDAP channel binding is disabled
807
+ * Requires GPO settings from SYSVOL
808
+ */
809
+ export function detectLdapChannelBindingDisabled(
810
+ gpoSettings: GpoSecuritySettings | null,
811
+ domain: ADDomain | null,
812
+ includeDetails: boolean
813
+ ): Finding {
814
+ if (gpoSettings && gpoSettings.ldapChannelBinding !== undefined) {
815
+ const bindingDisabled = gpoSettings.ldapChannelBinding === 0;
816
+
817
+ return {
818
+ type: 'LDAP_CHANNEL_BINDING_DISABLED',
819
+ severity: 'high',
820
+ category: 'advanced',
821
+ title: 'LDAP Channel Binding Not Required',
822
+ description:
823
+ 'LDAP channel binding is not required. This allows NTLM relay attacks against LDAPS connections even when signing is enabled.',
824
+ count: bindingDisabled ? 1 : 0,
825
+ affectedEntities: includeDetails && bindingDisabled && domain ? [domain.dn] : undefined,
826
+ details: bindingDisabled
827
+ ? {
828
+ recommendation: 'Configure "Domain controller: LDAP server channel binding token requirements" to "Always".',
829
+ currentSetting: gpoSettings.ldapChannelBinding,
830
+ requiredSetting: 2,
831
+ }
832
+ : undefined,
833
+ };
834
+ }
835
+
836
+ return {
837
+ type: 'LDAP_CHANNEL_BINDING_DISABLED',
838
+ severity: 'high',
839
+ category: 'advanced',
840
+ title: 'LDAP Channel Binding Configuration Unknown',
841
+ description: 'Unable to determine LDAP channel binding configuration. Manual review recommended.',
842
+ count: 0,
843
+ details: {
844
+ note: 'GPO/Registry settings not available. Check LdapEnforceChannelBinding registry value manually.',
845
+ },
846
+ };
847
+ }
848
+
849
+ /**
850
+ * Check if SMBv1 is enabled
851
+ * Requires GPO settings from SYSVOL
852
+ */
853
+ export function detectSmbV1Enabled(
854
+ gpoSettings: GpoSecuritySettings | null,
855
+ domain: ADDomain | null,
856
+ includeDetails: boolean
857
+ ): Finding {
858
+ if (gpoSettings && (gpoSettings.smbv1ServerEnabled !== undefined || gpoSettings.smbv1ClientEnabled !== undefined)) {
859
+ const smbv1Enabled = gpoSettings.smbv1ServerEnabled === true || gpoSettings.smbv1ClientEnabled === true;
860
+
861
+ return {
862
+ type: 'SMB_V1_ENABLED',
863
+ severity: 'high',
864
+ category: 'advanced',
865
+ title: 'SMBv1 Protocol Enabled',
866
+ description:
867
+ 'SMBv1 protocol is enabled. SMBv1 is deprecated and vulnerable to attacks like EternalBlue (WannaCry, NotPetya).',
868
+ count: smbv1Enabled ? 1 : 0,
869
+ affectedEntities: includeDetails && smbv1Enabled && domain ? [domain.dn] : undefined,
870
+ details: smbv1Enabled
871
+ ? {
872
+ recommendation: 'Disable SMBv1 on all systems. Use SMBv2/v3 instead.',
873
+ smbv1Server: gpoSettings.smbv1ServerEnabled,
874
+ smbv1Client: gpoSettings.smbv1ClientEnabled,
875
+ }
876
+ : undefined,
877
+ };
878
+ }
879
+
880
+ return {
881
+ type: 'SMB_V1_ENABLED',
882
+ severity: 'high',
883
+ category: 'advanced',
884
+ title: 'SMBv1 Configuration Unknown',
885
+ description: 'Unable to determine SMBv1 configuration. Manual review recommended.',
886
+ count: 0,
887
+ details: {
888
+ note: 'GPO/Registry settings not available. Check SMB1 registry values and Windows features manually.',
889
+ },
890
+ };
891
+ }
892
+
893
+ /**
894
+ * Check if SMB signing is disabled
895
+ * Requires GPO settings from SYSVOL
896
+ *
897
+ * SMB signing prevents man-in-the-middle attacks and NTLM relay attacks.
898
+ * Registry key: MACHINE\System\CurrentControlSet\Services\LanManServer\Parameters\RequireSecuritySignature
899
+ */
900
+ export function detectSmbSigningDisabled(
901
+ gpoSettings: GpoSecuritySettings | null,
902
+ domain: ADDomain | null,
903
+ includeDetails: boolean
904
+ ): Finding {
905
+ if (gpoSettings && gpoSettings.smbSigningRequired !== undefined) {
906
+ const signingDisabled = gpoSettings.smbSigningRequired === false;
907
+
908
+ return {
909
+ type: 'SMB_SIGNING_DISABLED',
910
+ severity: 'critical',
911
+ category: 'advanced',
912
+ title: 'SMB Signing Not Required',
913
+ description:
914
+ 'SMB server signing is not required. This allows man-in-the-middle attacks and NTLM relay attacks against SMB connections.',
915
+ count: signingDisabled ? 1 : 0,
916
+ affectedEntities: includeDetails && signingDisabled && domain ? [domain.dn] : undefined,
917
+ details: signingDisabled
918
+ ? {
919
+ recommendation: 'Configure "Microsoft network server: Digitally sign communications (always)" to Enabled.',
920
+ currentSetting: 'Not Required',
921
+ requiredSetting: 'Required',
922
+ smbServerSigning: gpoSettings.smbSigningRequired,
923
+ smbClientSigning: gpoSettings.smbClientSigningRequired,
924
+ }
925
+ : undefined,
926
+ };
927
+ }
928
+
929
+ // If GPO settings not available, assume vulnerable (Windows defaults don't require signing)
930
+ return {
931
+ type: 'SMB_SIGNING_DISABLED',
932
+ severity: 'critical',
933
+ category: 'advanced',
934
+ title: 'SMB Signing Not Configured in GPO',
935
+ description:
936
+ 'SMB signing is not configured via Group Policy. Windows defaults do not require SMB signing, making this environment vulnerable to NTLM relay attacks.',
937
+ count: 1,
938
+ affectedEntities: includeDetails && domain ? [domain.dn] : undefined,
939
+ details: {
940
+ recommendation:
941
+ 'Configure "Microsoft network server: Digitally sign communications (always)" via Group Policy.',
942
+ note: 'No GPO security template found. Windows defaults do not require SMB signing.',
943
+ },
944
+ };
945
+ }
946
+
947
+ /**
948
+ * Check if audit policy is weak/incomplete
949
+ * Requires GPO settings from SYSVOL
950
+ */
951
+ export function detectAuditPolicyWeak(
952
+ gpoSettings: GpoSecuritySettings | null,
953
+ domain: ADDomain | null,
954
+ includeDetails: boolean
955
+ ): Finding {
956
+ // Critical audit categories that should be enabled
957
+ const criticalAuditCategories = [
958
+ 'Account Logon',
959
+ 'Account Management',
960
+ 'Logon/Logoff',
961
+ 'Object Access',
962
+ 'Policy Change',
963
+ 'Privilege Use',
964
+ 'System',
965
+ ];
966
+
967
+ if (gpoSettings && gpoSettings.auditPolicies && gpoSettings.auditPolicies.length > 0) {
968
+ const configuredCategories = new Set(gpoSettings.auditPolicies.map((p) => p.category));
969
+ const missingCategories = criticalAuditCategories.filter((cat) => !configuredCategories.has(cat));
970
+
971
+ // Check if critical events are being audited
972
+ const hasWeakAudit = missingCategories.length > 0;
973
+
974
+ return {
975
+ type: 'AUDIT_POLICY_WEAK',
976
+ severity: 'medium',
977
+ category: 'advanced',
978
+ title: 'Audit Policy Incomplete',
979
+ description:
980
+ 'Domain audit policy does not cover all critical security events. Attacks may go undetected.',
981
+ count: hasWeakAudit ? 1 : 0,
982
+ affectedEntities: includeDetails && hasWeakAudit && domain ? [domain.dn] : undefined,
983
+ details: hasWeakAudit
984
+ ? {
985
+ recommendation: 'Configure Advanced Audit Policy to audit all critical security categories.',
986
+ missingCategories,
987
+ configuredCategories: Array.from(configuredCategories),
988
+ }
989
+ : undefined,
990
+ };
991
+ }
992
+
993
+ return {
994
+ type: 'AUDIT_POLICY_WEAK',
995
+ severity: 'medium',
996
+ category: 'advanced',
997
+ title: 'Audit Policy Configuration Unknown',
998
+ description: 'Unable to determine audit policy configuration. Manual review recommended.',
999
+ count: 0,
1000
+ details: {
1001
+ note: 'GPO audit settings not available. Check Advanced Audit Policy Configuration manually.',
1002
+ requiredCategories: criticalAuditCategories,
1003
+ },
1004
+ };
1005
+ }
1006
+
1007
+ /**
1008
+ * Check if PowerShell logging is disabled
1009
+ * Requires GPO settings from SYSVOL
1010
+ */
1011
+ export function detectPowershellLoggingDisabled(
1012
+ gpoSettings: GpoSecuritySettings | null,
1013
+ domain: ADDomain | null,
1014
+ includeDetails: boolean
1015
+ ): Finding {
1016
+ if (gpoSettings && gpoSettings.powershellLogging) {
1017
+ const logging = gpoSettings.powershellLogging;
1018
+ const loggingDisabled = !logging.moduleLogging && !logging.scriptBlockLogging;
1019
+
1020
+ return {
1021
+ type: 'POWERSHELL_LOGGING_DISABLED',
1022
+ severity: 'medium',
1023
+ category: 'advanced',
1024
+ title: 'PowerShell Logging Not Configured',
1025
+ description:
1026
+ 'PowerShell script block logging and module logging are not enabled. Malicious PowerShell activity will not be logged.',
1027
+ count: loggingDisabled ? 1 : 0,
1028
+ affectedEntities: includeDetails && loggingDisabled && domain ? [domain.dn] : undefined,
1029
+ details: loggingDisabled
1030
+ ? {
1031
+ recommendation:
1032
+ 'Enable "Turn on PowerShell Script Block Logging" and "Turn on Module Logging" via GPO.',
1033
+ moduleLogging: logging.moduleLogging,
1034
+ scriptBlockLogging: logging.scriptBlockLogging,
1035
+ transcription: logging.transcription,
1036
+ }
1037
+ : undefined,
1038
+ };
1039
+ }
1040
+
1041
+ return {
1042
+ type: 'POWERSHELL_LOGGING_DISABLED',
1043
+ severity: 'medium',
1044
+ category: 'advanced',
1045
+ title: 'PowerShell Logging Configuration Unknown',
1046
+ description: 'Unable to determine PowerShell logging configuration. Manual review recommended.',
1047
+ count: 0,
1048
+ details: {
1049
+ note: 'GPO/Registry settings not available. Check PowerShell logging GPO settings manually.',
1050
+ },
1051
+ };
1052
+ }
1053
+
1054
+ /**
1055
+ * Advanced detector options
1056
+ */
1057
+ /**
1058
+ * Detect modified dsHeuristics attribute
1059
+ *
1060
+ * dsHeuristics controls various AD behaviors. Non-default values may indicate
1061
+ * security weakening (e.g., allowing anonymous access, disabling list object mode).
1062
+ *
1063
+ * @param domain - Domain information
1064
+ * @param _includeDetails - Whether to include affected entity details
1065
+ * @returns Finding for DS_HEURISTICS_MODIFIED
1066
+ */
1067
+ export function detectDsHeuristicsModified(
1068
+ domain: ADDomain | null,
1069
+ _includeDetails: boolean
1070
+ ): Finding {
1071
+ // dsHeuristics is stored on CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration
1072
+ const dsHeuristics = domain ? (domain as Record<string, unknown>)['dsHeuristics'] as string | undefined : undefined;
1073
+
1074
+ // Default is empty or null - any value is a modification
1075
+ const isModified = dsHeuristics !== undefined && dsHeuristics !== null && dsHeuristics !== '';
1076
+
1077
+ // Check for specific dangerous settings
1078
+ const dangerousSettings: string[] = [];
1079
+ if (dsHeuristics) {
1080
+ // Position 7: fLDAPBlockAnonOps (0=block anonymous, 2=allow)
1081
+ if (dsHeuristics.length >= 7 && dsHeuristics[6] === '2') {
1082
+ dangerousSettings.push('Anonymous LDAP operations allowed (position 7)');
1083
+ }
1084
+ // Position 3: fDisableListObject (0=enabled, 1=disabled)
1085
+ if (dsHeuristics.length >= 3 && dsHeuristics[2] === '1') {
1086
+ dangerousSettings.push('List Object mode disabled (position 3)');
1087
+ }
1088
+ }
1089
+
1090
+ return {
1091
+ type: 'DS_HEURISTICS_MODIFIED',
1092
+ severity: 'medium',
1093
+ category: 'advanced',
1094
+ title: 'dsHeuristics Modified',
1095
+ description:
1096
+ 'The dsHeuristics attribute has been modified from defaults. ' +
1097
+ 'This may weaken AD security or enable dangerous features.',
1098
+ count: isModified ? 1 : 0,
1099
+ details: {
1100
+ currentValue: dsHeuristics || '(empty)',
1101
+ dangerousSettings: dangerousSettings.length > 0 ? dangerousSettings : undefined,
1102
+ recommendation:
1103
+ 'Review dsHeuristics value and document any intentional modifications.',
1104
+ },
1105
+ };
1106
+ }
1107
+
1108
+ /**
1109
+ * Detect modified AdminSDHolder permissions
1110
+ *
1111
+ * AdminSDHolder template is applied to protected accounts every 60 minutes.
1112
+ * Modified permissions on AdminSDHolder will propagate to all protected accounts.
1113
+ *
1114
+ * @param domain - Domain information
1115
+ * @param _includeDetails - Whether to include affected entity details
1116
+ * @returns Finding for ADMIN_SD_HOLDER_MODIFIED
1117
+ */
1118
+ export function detectAdminSdHolderModified(
1119
+ domain: ADDomain | null,
1120
+ _includeDetails: boolean
1121
+ ): Finding {
1122
+ // This would require reading the nTSecurityDescriptor of AdminSDHolder
1123
+ // For now, check if domain has indicators of modification
1124
+ // A proper check would compare against known-good template
1125
+ const adminSdHolderInfo = domain
1126
+ ? (domain as Record<string, unknown>)['adminSDHolderModified'] as boolean | undefined
1127
+ : undefined;
1128
+
1129
+ return {
1130
+ type: 'ADMIN_SD_HOLDER_MODIFIED',
1131
+ severity: 'high',
1132
+ category: 'advanced',
1133
+ title: 'AdminSDHolder Review Required',
1134
+ description:
1135
+ 'AdminSDHolder permissions should be reviewed. Modifications propagate to all protected accounts ' +
1136
+ '(Domain Admins, Enterprise Admins, etc.) via SDProp process.',
1137
+ count: adminSdHolderInfo ? 1 : 0,
1138
+ details: {
1139
+ recommendation:
1140
+ 'Compare AdminSDHolder ACL against baseline. Look for non-standard principals with permissions.',
1141
+ checkCommand: 'Get-ADObject "CN=AdminSDHolder,CN=System,DC=..." -Properties nTSecurityDescriptor',
1142
+ },
1143
+ };
1144
+ }
1145
+
1146
+ /**
1147
+ * Detect Exchange privilege escalation paths
1148
+ *
1149
+ * Exchange security groups often have dangerous permissions that can be
1150
+ * abused for privilege escalation (WriteDacl on domain, etc.).
1151
+ *
1152
+ * @param users - Array of AD users
1153
+ * @param includeDetails - Whether to include affected entity details
1154
+ * @returns Finding for EXCHANGE_PRIV_ESC_PATH
1155
+ */
1156
+ export function detectExchangePrivEscPath(
1157
+ users: ADUser[],
1158
+ includeDetails: boolean
1159
+ ): Finding {
1160
+ // Exchange groups with dangerous permissions
1161
+ const exchangeGroups = [
1162
+ 'Exchange Trusted Subsystem',
1163
+ 'Exchange Windows Permissions',
1164
+ 'Organization Management',
1165
+ 'Exchange Servers',
1166
+ ];
1167
+
1168
+ // Find users in Exchange groups that might indicate privilege escalation risk
1169
+ const affected = users.filter((u) => {
1170
+ if (!u.enabled || !u.memberOf) return false;
1171
+ return u.memberOf.some((dn) =>
1172
+ exchangeGroups.some((eg) => dn.toLowerCase().includes(eg.toLowerCase()))
1173
+ );
1174
+ });
1175
+
1176
+ return {
1177
+ type: 'EXCHANGE_PRIV_ESC_PATH',
1178
+ severity: 'critical',
1179
+ category: 'advanced',
1180
+ title: 'Exchange Privilege Escalation Risk',
1181
+ description:
1182
+ 'Users in Exchange security groups with potentially dangerous permissions. ' +
1183
+ 'Exchange Trusted Subsystem has WriteDacl on domain by default (CVE-2019-1166).',
1184
+ count: affected.length,
1185
+ affectedEntities: includeDetails ? toAffectedUserEntities(affected) : undefined,
1186
+ details: {
1187
+ exchangeGroups: exchangeGroups,
1188
+ recommendation:
1189
+ 'Review Exchange group permissions on domain head. Apply PrivExchange mitigations.',
1190
+ reference: 'CVE-2019-1166, PrivExchange',
1191
+ },
1192
+ };
1193
+ }
1194
+
1195
+ export interface AdvancedDetectorOptions {
1196
+ /** GPO security settings from SYSVOL */
1197
+ gpoSettings?: GpoSecuritySettings | null;
1198
+ /** Whether anonymous LDAP access is allowed */
1199
+ anonymousAccessAllowed?: boolean;
1200
+ }
1201
+
1202
+ /**
1203
+ * Detect all advanced vulnerabilities
1204
+ */
1205
+ export function detectAdvancedVulnerabilities(
1206
+ users: ADUser[],
1207
+ computers: ADComputer[],
1208
+ domain: ADDomain | null,
1209
+ templates: any[] = [],
1210
+ cas: any[] = [],
1211
+ fsps: any[] = [],
1212
+ includeDetails: boolean,
1213
+ options: AdvancedDetectorOptions = {}
1214
+ ): Finding[] {
1215
+ const { gpoSettings = null, anonymousAccessAllowed = false } = options;
1216
+
1217
+ return [
1218
+ // Critical
1219
+ detectShadowCredentials(users, includeDetails),
1220
+ detectRbcdAbuse(users, includeDetails),
1221
+ // Critical (GPO-based) - Story 1.1
1222
+ detectLdapSigningDisabled(gpoSettings, domain, includeDetails),
1223
+ detectSmbSigningDisabled(gpoSettings, domain, includeDetails),
1224
+ // High
1225
+ detectEsc1VulnerableTemplate(templates, includeDetails),
1226
+ detectEsc2AnyPurpose(templates, includeDetails),
1227
+ detectEsc3EnrollmentAgent(templates, includeDetails),
1228
+ detectEsc4VulnerableTemplateAcl(templates, includeDetails),
1229
+ detectEsc6EditfAttributeSubjectAltName2(cas, includeDetails),
1230
+ detectLapsPasswordReadable(computers, includeDetails),
1231
+ detectReplicationRights(users, includeDetails),
1232
+ detectDcsyncCapable(users, includeDetails),
1233
+ // High (domain)
1234
+ detectMachineAccountQuotaHigh(domain, includeDetails),
1235
+ // High (GPO-based)
1236
+ detectLdapChannelBindingDisabled(gpoSettings, domain, includeDetails),
1237
+ detectSmbV1Enabled(gpoSettings, domain, includeDetails),
1238
+ // Medium
1239
+ detectEsc8HttpEnrollment(cas, includeDetails),
1240
+ detectLapsNotDeployed(computers, includeDetails),
1241
+ detectLapsLegacyAttribute(computers, includeDetails),
1242
+ detectDuplicateSpn(users, includeDetails),
1243
+ detectWeakPasswordPolicy(domain, includeDetails),
1244
+ detectWeakKerberosPolicy(domain, includeDetails),
1245
+ detectMachineAccountQuotaAbuse(domain, includeDetails),
1246
+ detectDelegationPrivilege(users, includeDetails),
1247
+ detectForeignSecurityPrincipals(fsps, includeDetails),
1248
+ detectNtlmRelayOpportunity(domain, includeDetails),
1249
+ detectAdcsWeakPermissions(templates, includeDetails),
1250
+ detectDangerousLogonScripts(users, includeDetails),
1251
+ // Medium (Phase 1B)
1252
+ detectRecycleBinDisabled(domain, includeDetails),
1253
+ detectAnonymousLdapAccess(anonymousAccessAllowed, domain, includeDetails),
1254
+ detectAuditPolicyWeak(gpoSettings, domain, includeDetails),
1255
+ detectPowershellLoggingDisabled(gpoSettings, domain, includeDetails),
1256
+ // Low
1257
+ detectLapsPasswordSet(computers, includeDetails),
1258
+ detectLapsPasswordLeaked(computers, includeDetails),
1259
+ // Phase 4: Advanced detections
1260
+ detectDsHeuristicsModified(domain, includeDetails),
1261
+ detectAdminSdHolderModified(domain, includeDetails),
1262
+ detectExchangePrivEscPath(users, includeDetails),
1263
+ ].filter(
1264
+ (finding) =>
1265
+ finding.count > 0 ||
1266
+ // Always include critical signing findings (even with count=0) for visibility
1267
+ finding.type === 'SMB_SIGNING_DISABLED' ||
1268
+ finding.type === 'LDAP_SIGNING_DISABLED'
1269
+ );
1270
+ }