@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,881 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectSensitiveDelegation = detectSensitiveDelegation;
4
+ exports.detectDisabledAccountInAdminGroup = detectDisabledAccountInAdminGroup;
5
+ exports.detectExpiredAccountInAdminGroup = detectExpiredAccountInAdminGroup;
6
+ exports.detectSidHistory = detectSidHistory;
7
+ exports.detectNotInProtectedUsers = detectNotInProtectedUsers;
8
+ exports.detectDomainAdminInDescription = detectDomainAdminInDescription;
9
+ exports.detectBackupOperatorsMember = detectBackupOperatorsMember;
10
+ exports.detectAccountOperatorsMember = detectAccountOperatorsMember;
11
+ exports.detectServerOperatorsMember = detectServerOperatorsMember;
12
+ exports.detectPrintOperatorsMember = detectPrintOperatorsMember;
13
+ exports.detectInactive365Days = detectInactive365Days;
14
+ exports.detectStaleAccount = detectStaleAccount;
15
+ exports.detectNeverLoggedOn = detectNeverLoggedOn;
16
+ exports.detectAccountExpireSoon = detectAccountExpireSoon;
17
+ exports.detectAdminLogonCountLow = detectAdminLogonCountLow;
18
+ exports.detectTestAccount = detectTestAccount;
19
+ exports.detectSharedAccount = detectSharedAccount;
20
+ exports.detectSmartcardNotRequired = detectSmartcardNotRequired;
21
+ exports.detectPrimaryGroupIdSpoofing = detectPrimaryGroupIdSpoofing;
22
+ exports.detectServiceAccountWithSpn = detectServiceAccountWithSpn;
23
+ exports.detectServiceAccountNaming = detectServiceAccountNaming;
24
+ exports.detectServiceAccountOldPassword = detectServiceAccountOldPassword;
25
+ exports.detectServiceAccountPrivileged = detectServiceAccountPrivileged;
26
+ exports.detectServiceAccountNoPreauth = detectServiceAccountNoPreauth;
27
+ exports.detectServiceAccountWeakEncryption = detectServiceAccountWeakEncryption;
28
+ exports.detectAdminCountOrphaned = detectAdminCountOrphaned;
29
+ exports.detectPrivilegedAccountSpn = detectPrivilegedAccountSpn;
30
+ exports.detectAdminNoSmartcard = detectAdminNoSmartcard;
31
+ exports.detectServiceAccountInteractive = detectServiceAccountInteractive;
32
+ exports.detectServiceAccountVulnerabilities = detectServiceAccountVulnerabilities;
33
+ exports.detectReplicaDirectoryChanges = detectReplicaDirectoryChanges;
34
+ exports.detectDangerousBuiltinMembership = detectDangerousBuiltinMembership;
35
+ exports.detectLockedAccountAdmin = detectLockedAccountAdmin;
36
+ exports.detectAccountsVulnerabilities = detectAccountsVulnerabilities;
37
+ const entity_converter_1 = require("../../../../utils/entity-converter");
38
+ function detectSensitiveDelegation(users, includeDetails) {
39
+ const privilegedGroups = [
40
+ 'Domain Admins',
41
+ 'Enterprise Admins',
42
+ 'Schema Admins',
43
+ 'Administrators',
44
+ ];
45
+ const affected = users.filter((u) => {
46
+ if (!u.userAccountControl || !u.memberOf)
47
+ return false;
48
+ const hasUnconstrainedDeleg = (u.userAccountControl & 0x80000) !== 0;
49
+ const isPrivileged = u.memberOf.some((dn) => privilegedGroups.some((group) => dn.includes(`CN=${group}`)));
50
+ return hasUnconstrainedDeleg && isPrivileged;
51
+ });
52
+ return {
53
+ type: 'SENSITIVE_DELEGATION',
54
+ severity: 'critical',
55
+ category: 'accounts',
56
+ title: 'Sensitive Account with Delegation',
57
+ description: 'Privileged accounts (Domain/Enterprise Admins) with unconstrained delegation. Extreme security risk.',
58
+ count: affected.length,
59
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
60
+ };
61
+ }
62
+ function detectDisabledAccountInAdminGroup(users, includeDetails) {
63
+ const adminGroups = ['Domain Admins', 'Enterprise Admins', 'Schema Admins'];
64
+ const affected = users.filter((u) => {
65
+ if (!u.userAccountControl || !u.memberOf)
66
+ return false;
67
+ const isDisabled = (u.userAccountControl & 0x2) !== 0;
68
+ const isInAdminGroup = u.memberOf.some((dn) => adminGroups.some((group) => dn.includes(`CN=${group}`)));
69
+ return isDisabled && isInAdminGroup;
70
+ });
71
+ return {
72
+ type: 'DISABLED_ACCOUNT_IN_ADMIN_GROUP',
73
+ severity: 'high',
74
+ category: 'accounts',
75
+ title: 'Disabled Account in Admin Group',
76
+ description: 'Disabled user accounts still present in privileged groups. Should be removed immediately.',
77
+ count: affected.length,
78
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
79
+ };
80
+ }
81
+ function detectExpiredAccountInAdminGroup(users, includeDetails) {
82
+ const adminGroups = ['Domain Admins', 'Enterprise Admins', 'Schema Admins'];
83
+ const now = Date.now();
84
+ const affected = users.filter((u) => {
85
+ if (!u.memberOf)
86
+ return false;
87
+ const accountExpires = u['accountExpires'];
88
+ const isExpired = accountExpires && accountExpires.getTime() < now;
89
+ const isInAdminGroup = u.memberOf.some((dn) => adminGroups.some((group) => dn.includes(`CN=${group}`)));
90
+ return isExpired && isInAdminGroup;
91
+ });
92
+ return {
93
+ type: 'EXPIRED_ACCOUNT_IN_ADMIN_GROUP',
94
+ severity: 'high',
95
+ category: 'accounts',
96
+ title: 'Expired Account in Admin Group',
97
+ description: 'Expired user accounts still present in privileged groups. Should be removed immediately.',
98
+ count: affected.length,
99
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
100
+ };
101
+ }
102
+ function detectSidHistory(users, includeDetails) {
103
+ const affected = users.filter((u) => {
104
+ const userObj = u;
105
+ const sidHistory = userObj['sIDHistory'] ??
106
+ userObj['sidhistory'] ??
107
+ userObj['SIDHistory'] ??
108
+ userObj['sidHistory'];
109
+ if (!sidHistory)
110
+ return false;
111
+ if (Array.isArray(sidHistory)) {
112
+ return sidHistory.length > 0;
113
+ }
114
+ return !!sidHistory;
115
+ });
116
+ return {
117
+ type: 'SID_HISTORY',
118
+ severity: 'high',
119
+ category: 'accounts',
120
+ title: 'SID History Present',
121
+ description: 'User accounts with sIDHistory attribute. Can be abused for privilege escalation.',
122
+ count: affected.length,
123
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
124
+ };
125
+ }
126
+ function detectNotInProtectedUsers(users, includeDetails) {
127
+ const privilegedGroups = ['Domain Admins', 'Enterprise Admins', 'Schema Admins'];
128
+ const affected = users.filter((u) => {
129
+ if (!u.memberOf)
130
+ return false;
131
+ const isPrivileged = u.memberOf.some((dn) => privilegedGroups.some((group) => dn.includes(`CN=${group}`)));
132
+ const isInProtectedUsers = u.memberOf.some((dn) => dn.includes('CN=Protected Users'));
133
+ return isPrivileged && !isInProtectedUsers;
134
+ });
135
+ return {
136
+ type: 'NOT_IN_PROTECTED_USERS',
137
+ severity: 'high',
138
+ category: 'accounts',
139
+ title: 'Not in Protected Users Group',
140
+ description: 'Privileged accounts not in Protected Users group. Missing additional security protections.',
141
+ count: affected.length,
142
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
143
+ };
144
+ }
145
+ function detectDomainAdminInDescription(users, includeDetails) {
146
+ const sensitiveKeywords = [
147
+ /domain\s*admin/i,
148
+ /enterprise\s*admin/i,
149
+ /administrator/i,
150
+ /admin\s*account/i,
151
+ /privileged/i,
152
+ ];
153
+ const affected = users.filter((u) => {
154
+ const description = (0, entity_converter_1.ldapAttrToString)(u['description']);
155
+ if (!description)
156
+ return false;
157
+ return sensitiveKeywords.some((pattern) => pattern.test(description));
158
+ });
159
+ return {
160
+ type: 'DOMAIN_ADMIN_IN_DESCRIPTION',
161
+ severity: 'high',
162
+ category: 'accounts',
163
+ title: 'Sensitive Terms in Description',
164
+ description: 'User accounts with admin/privileged keywords in description field. Information disclosure.',
165
+ count: affected.length,
166
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
167
+ };
168
+ }
169
+ function detectBackupOperatorsMember(users, includeDetails) {
170
+ const affected = users.filter((u) => {
171
+ if (!u.memberOf)
172
+ return false;
173
+ return u.memberOf.some((dn) => dn.includes('CN=Backup Operators'));
174
+ });
175
+ return {
176
+ type: 'BACKUP_OPERATORS_MEMBER',
177
+ severity: 'high',
178
+ category: 'accounts',
179
+ title: 'Backup Operators Member',
180
+ description: 'Users in Backup Operators group. Can backup/restore files and bypass ACLs.',
181
+ count: affected.length,
182
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
183
+ };
184
+ }
185
+ function detectAccountOperatorsMember(users, includeDetails) {
186
+ const affected = users.filter((u) => {
187
+ if (!u.memberOf)
188
+ return false;
189
+ return u.memberOf.some((dn) => dn.includes('CN=Account Operators'));
190
+ });
191
+ return {
192
+ type: 'ACCOUNT_OPERATORS_MEMBER',
193
+ severity: 'high',
194
+ category: 'accounts',
195
+ title: 'Account Operators Member',
196
+ description: 'Users in Account Operators group. Can create/modify user accounts.',
197
+ count: affected.length,
198
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
199
+ };
200
+ }
201
+ function detectServerOperatorsMember(users, includeDetails) {
202
+ const affected = users.filter((u) => {
203
+ if (!u.memberOf)
204
+ return false;
205
+ return u.memberOf.some((dn) => dn.includes('CN=Server Operators'));
206
+ });
207
+ return {
208
+ type: 'SERVER_OPERATORS_MEMBER',
209
+ severity: 'high',
210
+ category: 'accounts',
211
+ title: 'Server Operators Member',
212
+ description: 'Users in Server Operators group. Can manage domain controllers.',
213
+ count: affected.length,
214
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
215
+ };
216
+ }
217
+ function detectPrintOperatorsMember(users, includeDetails) {
218
+ const affected = users.filter((u) => {
219
+ if (!u.memberOf)
220
+ return false;
221
+ return u.memberOf.some((dn) => dn.includes('CN=Print Operators'));
222
+ });
223
+ return {
224
+ type: 'PRINT_OPERATORS_MEMBER',
225
+ severity: 'high',
226
+ category: 'accounts',
227
+ title: 'Print Operators Member',
228
+ description: 'Users in Print Operators group. Can load drivers and manage printers on DCs.',
229
+ count: affected.length,
230
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
231
+ };
232
+ }
233
+ function detectInactive365Days(users, includeDetails) {
234
+ const now = Date.now();
235
+ const oneYearAgo = now - 365 * 24 * 60 * 60 * 1000;
236
+ const affected = users.filter((u) => {
237
+ if (!u.lastLogon)
238
+ return false;
239
+ return u.lastLogon.getTime() < oneYearAgo;
240
+ });
241
+ return {
242
+ type: 'INACTIVE_365_DAYS',
243
+ severity: 'medium',
244
+ category: 'accounts',
245
+ title: 'Inactive 365+ Days',
246
+ description: 'User accounts inactive for 365+ days. Should be disabled or deleted.',
247
+ count: affected.length,
248
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
249
+ };
250
+ }
251
+ function detectStaleAccount(users, includeDetails) {
252
+ const now = Date.now();
253
+ const sixMonthsAgo = now - 180 * 24 * 60 * 60 * 1000;
254
+ const affected = users.filter((u) => {
255
+ if (!u.enabled)
256
+ return false;
257
+ if (!u.lastLogon)
258
+ return false;
259
+ const lastLogonTime = u.lastLogon instanceof Date ? u.lastLogon.getTime() : new Date(u.lastLogon).getTime();
260
+ if (isNaN(lastLogonTime))
261
+ return false;
262
+ return lastLogonTime < sixMonthsAgo;
263
+ });
264
+ return {
265
+ type: 'STALE_ACCOUNT',
266
+ severity: 'high',
267
+ category: 'accounts',
268
+ title: 'Stale Account (180+ Days)',
269
+ description: 'Enabled user accounts inactive for 180+ days. Stale accounts increase attack surface and should be reviewed.',
270
+ count: affected.length,
271
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
272
+ };
273
+ }
274
+ function detectNeverLoggedOn(users, includeDetails) {
275
+ const affected = users.filter((u) => {
276
+ if (!u.enabled)
277
+ return false;
278
+ return !u.lastLogon;
279
+ });
280
+ return {
281
+ type: 'NEVER_LOGGED_ON',
282
+ severity: 'medium',
283
+ category: 'accounts',
284
+ title: 'Never Logged On',
285
+ description: 'Enabled user accounts that have never logged into the domain. May indicate orphaned accounts, provisioning issues, or unused accounts that should be disabled.',
286
+ count: affected.length,
287
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
288
+ };
289
+ }
290
+ function filetimeToDate(filetime) {
291
+ if (!filetime)
292
+ return null;
293
+ const ft = typeof filetime === 'string' ? BigInt(filetime) : BigInt(filetime);
294
+ if (ft === BigInt(0) || ft === BigInt('9223372036854775807'))
295
+ return null;
296
+ const ms = Number(ft / BigInt(10000)) - 11644473600000;
297
+ return new Date(ms);
298
+ }
299
+ function detectAccountExpireSoon(users, includeDetails) {
300
+ const now = Date.now();
301
+ const thirtyDaysFromNow = now + 30 * 24 * 60 * 60 * 1000;
302
+ const affected = users.filter((u) => {
303
+ if (!u.enabled)
304
+ return false;
305
+ const expiresDate = filetimeToDate(u.accountExpires);
306
+ if (!expiresDate)
307
+ return false;
308
+ return expiresDate.getTime() > now && expiresDate.getTime() <= thirtyDaysFromNow;
309
+ });
310
+ return {
311
+ type: 'ACCOUNT_EXPIRE_SOON',
312
+ severity: 'medium',
313
+ category: 'accounts',
314
+ title: 'Account Expiring Soon',
315
+ description: 'User accounts set to expire within the next 30 days. Review if these expirations are intentional or if accounts need to be extended.',
316
+ count: affected.length,
317
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
318
+ };
319
+ }
320
+ function detectAdminLogonCountLow(users, includeDetails) {
321
+ const affected = users.filter((u) => {
322
+ if (!u.enabled)
323
+ return false;
324
+ if (u.adminCount !== 1)
325
+ return false;
326
+ const logonCount = u['logonCount'];
327
+ return logonCount !== undefined && logonCount < 5;
328
+ });
329
+ return {
330
+ type: 'ADMIN_LOGON_COUNT_LOW',
331
+ severity: 'low',
332
+ category: 'accounts',
333
+ title: 'Admin Account with Low Logon Count',
334
+ description: 'Administrative accounts (adminCount=1) with fewer than 5 logons. May indicate unused privileged accounts that should be reviewed or disabled.',
335
+ count: affected.length,
336
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
337
+ };
338
+ }
339
+ function detectTestAccount(users, includeDetails) {
340
+ const testPatterns = [/^test/i, /test$/i, /_test/i, /\.test/i, /^demo/i, /^temp/i];
341
+ const affected = users.filter((u) => {
342
+ return testPatterns.some((pattern) => pattern.test(u.sAMAccountName));
343
+ });
344
+ return {
345
+ type: 'TEST_ACCOUNT',
346
+ severity: 'medium',
347
+ category: 'accounts',
348
+ title: 'Test Account',
349
+ description: 'User accounts with test/demo/temp naming. Should be removed from production.',
350
+ count: affected.length,
351
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
352
+ };
353
+ }
354
+ function detectSharedAccount(users, includeDetails) {
355
+ const sharedPatterns = [/^shared/i, /^common/i, /^generic/i, /^service/i, /^svc/i];
356
+ const affected = users.filter((u) => {
357
+ return sharedPatterns.some((pattern) => pattern.test(u.sAMAccountName));
358
+ });
359
+ return {
360
+ type: 'SHARED_ACCOUNT',
361
+ severity: 'medium',
362
+ category: 'accounts',
363
+ title: 'Shared Account',
364
+ description: 'User accounts with shared/generic naming. Prevents proper accountability.',
365
+ count: affected.length,
366
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
367
+ };
368
+ }
369
+ function detectSmartcardNotRequired(users, includeDetails) {
370
+ const affected = users.filter((u) => {
371
+ if (!u.enabled)
372
+ return false;
373
+ if (!u.adminCount || u.adminCount !== 1)
374
+ return false;
375
+ const uac = u.userAccountControl || 0;
376
+ return (uac & 0x40000) === 0;
377
+ });
378
+ return {
379
+ type: 'SMARTCARD_NOT_REQUIRED',
380
+ severity: 'medium',
381
+ category: 'accounts',
382
+ title: 'Smartcard Not Required',
383
+ description: 'Privileged accounts (adminCount=1) without smartcard requirement. ' +
384
+ 'High-value accounts should require strong authentication.',
385
+ count: affected.length,
386
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
387
+ };
388
+ }
389
+ function detectPrimaryGroupIdSpoofing(users, includeDetails) {
390
+ const affected = users.filter((u) => {
391
+ const primaryGroupId = u.primaryGroupID;
392
+ if (!primaryGroupId)
393
+ return false;
394
+ return primaryGroupId !== 513;
395
+ });
396
+ return {
397
+ type: 'PRIMARYGROUPID_SPOOFING',
398
+ severity: 'medium',
399
+ category: 'accounts',
400
+ title: 'primaryGroupID Spoofing',
401
+ description: 'User accounts with non-standard primaryGroupID. Can be used to hide group membership.',
402
+ count: affected.length,
403
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
404
+ };
405
+ }
406
+ const SERVICE_ACCOUNT_PATTERNS = [
407
+ /^svc[_-]/i,
408
+ /[_-]svc$/i,
409
+ /^service[_-]/i,
410
+ /[_-]service$/i,
411
+ /^sa[_-]/i,
412
+ /[_-]sa$/i,
413
+ /^app[_-]/i,
414
+ /^sql[_-]/i,
415
+ /^iis[_-]/i,
416
+ /^web[_-]/i,
417
+ /^batch[_-]/i,
418
+ /^task[_-]/i,
419
+ /^job[_-]/i,
420
+ /^daemon[_-]/i,
421
+ /^agent[_-]/i,
422
+ ];
423
+ function getServicePrincipalNames(user) {
424
+ const spn = user['servicePrincipalName'];
425
+ if (!spn)
426
+ return [];
427
+ if (Array.isArray(spn))
428
+ return spn;
429
+ return [spn];
430
+ }
431
+ function isServiceAccount(user) {
432
+ const spns = getServicePrincipalNames(user);
433
+ if (spns.length > 0) {
434
+ return true;
435
+ }
436
+ return SERVICE_ACCOUNT_PATTERNS.some((pattern) => pattern.test(user.sAMAccountName));
437
+ }
438
+ function detectServiceAccountWithSpn(users, includeDetails) {
439
+ const affected = users.filter((u) => {
440
+ const spns = getServicePrincipalNames(u);
441
+ if (spns.length === 0)
442
+ return false;
443
+ if (u.userAccountControl && (u.userAccountControl & 0x2) !== 0)
444
+ return false;
445
+ return true;
446
+ });
447
+ return {
448
+ type: 'SERVICE_ACCOUNT_WITH_SPN',
449
+ severity: 'medium',
450
+ category: 'accounts',
451
+ title: 'Service Account with SPN (Kerberoasting Target)',
452
+ description: 'User accounts with Service Principal Name configured. These accounts are targets for Kerberoasting attacks where attackers request TGS tickets and crack them offline.',
453
+ count: affected.length,
454
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
455
+ details: affected.length > 0
456
+ ? {
457
+ recommendation: 'Use gMSA (Group Managed Service Accounts) instead. For existing accounts, ensure strong passwords (25+ chars) and regular rotation.',
458
+ spnCount: affected.reduce((sum, u) => sum + getServicePrincipalNames(u).length, 0),
459
+ }
460
+ : undefined,
461
+ };
462
+ }
463
+ function detectServiceAccountNaming(users, includeDetails) {
464
+ const affected = users.filter((u) => {
465
+ const spns = getServicePrincipalNames(u);
466
+ if (spns.length > 0)
467
+ return false;
468
+ if (u.userAccountControl && (u.userAccountControl & 0x2) !== 0)
469
+ return false;
470
+ return SERVICE_ACCOUNT_PATTERNS.some((pattern) => pattern.test(u.sAMAccountName));
471
+ });
472
+ return {
473
+ type: 'SERVICE_ACCOUNT_NAMING',
474
+ severity: 'low',
475
+ category: 'accounts',
476
+ title: 'Service Account by Naming Convention',
477
+ description: 'User accounts matching service account naming patterns (svc_, _svc, service, etc.) without SPN. Review if these are actual service accounts.',
478
+ count: affected.length,
479
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
480
+ };
481
+ }
482
+ function detectServiceAccountOldPassword(users, includeDetails) {
483
+ const now = Date.now();
484
+ const oneYearAgo = now - 365 * 24 * 60 * 60 * 1000;
485
+ const affected = users.filter((u) => {
486
+ if (!isServiceAccount(u))
487
+ return false;
488
+ if (u.userAccountControl && (u.userAccountControl & 0x2) !== 0)
489
+ return false;
490
+ if (!u.passwordLastSet)
491
+ return true;
492
+ return u.passwordLastSet.getTime() < oneYearAgo;
493
+ });
494
+ return {
495
+ type: 'SERVICE_ACCOUNT_OLD_PASSWORD',
496
+ severity: 'high',
497
+ category: 'accounts',
498
+ title: 'Service Account with Old Password',
499
+ description: 'Service accounts with passwords not changed in over 1 year. These accounts are high-value targets and passwords should be rotated regularly.',
500
+ count: affected.length,
501
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
502
+ details: affected.length > 0
503
+ ? {
504
+ recommendation: 'Rotate service account passwords every 90 days or migrate to gMSA for automatic password management.',
505
+ }
506
+ : undefined,
507
+ };
508
+ }
509
+ function detectServiceAccountPrivileged(users, includeDetails) {
510
+ const privilegedGroups = [
511
+ 'Domain Admins',
512
+ 'Enterprise Admins',
513
+ 'Schema Admins',
514
+ 'Administrators',
515
+ 'Backup Operators',
516
+ 'Account Operators',
517
+ 'Server Operators',
518
+ ];
519
+ const affected = users.filter((u) => {
520
+ if (!isServiceAccount(u))
521
+ return false;
522
+ if (u.userAccountControl && (u.userAccountControl & 0x2) !== 0)
523
+ return false;
524
+ if (!u.memberOf)
525
+ return false;
526
+ return u.memberOf.some((dn) => privilegedGroups.some((group) => dn.includes(`CN=${group}`)));
527
+ });
528
+ return {
529
+ type: 'SERVICE_ACCOUNT_PRIVILEGED',
530
+ severity: 'critical',
531
+ category: 'accounts',
532
+ title: 'Service Account in Privileged Group',
533
+ description: 'Service accounts with membership in privileged groups (Domain Admins, etc.). If compromised, attackers gain full domain control.',
534
+ count: affected.length,
535
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
536
+ details: affected.length > 0
537
+ ? {
538
+ recommendation: 'Remove service accounts from privileged groups. Grant only the minimum permissions needed for the service to function.',
539
+ }
540
+ : undefined,
541
+ };
542
+ }
543
+ function detectServiceAccountNoPreauth(users, includeDetails) {
544
+ const DONT_REQUIRE_PREAUTH = 0x400000;
545
+ const affected = users.filter((u) => {
546
+ if (!isServiceAccount(u))
547
+ return false;
548
+ if (!u.userAccountControl)
549
+ return false;
550
+ if ((u.userAccountControl & 0x2) !== 0)
551
+ return false;
552
+ return (u.userAccountControl & DONT_REQUIRE_PREAUTH) !== 0;
553
+ });
554
+ return {
555
+ type: 'SERVICE_ACCOUNT_NO_PREAUTH',
556
+ severity: 'high',
557
+ category: 'accounts',
558
+ title: 'Service Account Without Pre-Authentication (AS-REP Roasting)',
559
+ description: 'Service accounts with "Do not require Kerberos pre-authentication" enabled. Attackers can request AS-REP tickets and crack them offline.',
560
+ count: affected.length,
561
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
562
+ details: affected.length > 0
563
+ ? {
564
+ recommendation: 'Enable Kerberos pre-authentication for all service accounts.',
565
+ }
566
+ : undefined,
567
+ };
568
+ }
569
+ function detectServiceAccountWeakEncryption(users, includeDetails) {
570
+ const affected = users.filter((u) => {
571
+ if (!isServiceAccount(u))
572
+ return false;
573
+ if (u.userAccountControl && (u.userAccountControl & 0x2) !== 0)
574
+ return false;
575
+ const encTypes = u['msDS-SupportedEncryptionTypes'];
576
+ if (!encTypes)
577
+ return false;
578
+ const encTypesNum = typeof encTypes === 'string' ? parseInt(encTypes, 10) : encTypes;
579
+ const hasOnlyWeak = (encTypesNum & 0x7) !== 0 && (encTypesNum & 0x18) === 0;
580
+ return hasOnlyWeak;
581
+ });
582
+ return {
583
+ type: 'SERVICE_ACCOUNT_WEAK_ENCRYPTION',
584
+ severity: 'medium',
585
+ category: 'accounts',
586
+ title: 'Service Account Using Weak Kerberos Encryption',
587
+ description: 'Service accounts configured to use only weak Kerberos encryption (DES/RC4) without AES. Makes offline cracking easier.',
588
+ count: affected.length,
589
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
590
+ details: affected.length > 0
591
+ ? {
592
+ recommendation: 'Enable AES128 and AES256 encryption for all service accounts.',
593
+ }
594
+ : undefined,
595
+ };
596
+ }
597
+ function detectAdminCountOrphaned(users, includeDetails) {
598
+ const adminGroups = [
599
+ 'Domain Admins',
600
+ 'Enterprise Admins',
601
+ 'Schema Admins',
602
+ 'Administrators',
603
+ 'Account Operators',
604
+ 'Server Operators',
605
+ 'Backup Operators',
606
+ 'Print Operators',
607
+ ];
608
+ const affected = users.filter((u) => {
609
+ if (u.adminCount !== 1)
610
+ return false;
611
+ const memberOf = u['memberOf'];
612
+ if (!memberOf || memberOf.length === 0)
613
+ return true;
614
+ const isInAdminGroup = memberOf.some((dn) => adminGroups.some((group) => dn.toLowerCase().includes(`cn=${group.toLowerCase()}`)));
615
+ return !isInAdminGroup;
616
+ });
617
+ return {
618
+ type: 'ADMIN_COUNT_ORPHANED',
619
+ severity: 'medium',
620
+ category: 'accounts',
621
+ title: 'Orphaned AdminCount Flag',
622
+ description: 'Accounts with adminCount=1 but not in any privileged group. This may indicate removed admins that still have residual privileges or SDProp protection.',
623
+ count: affected.length,
624
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
625
+ details: affected.length > 0
626
+ ? {
627
+ recommendation: 'Review these accounts. If no longer admins, clear adminCount flag and reset ACLs to allow proper inheritance.',
628
+ impact: 'Accounts may still have protected ACLs preventing proper management.',
629
+ }
630
+ : undefined,
631
+ };
632
+ }
633
+ function detectPrivilegedAccountSpn(users, includeDetails) {
634
+ const affected = users.filter((u) => {
635
+ if (u.adminCount !== 1)
636
+ return false;
637
+ if (!u.enabled)
638
+ return false;
639
+ const spn = u['servicePrincipalName'];
640
+ const hasSPN = spn && Array.isArray(spn) && spn.length > 0;
641
+ return hasSPN;
642
+ });
643
+ return {
644
+ type: 'PRIVILEGED_ACCOUNT_SPN',
645
+ severity: 'high',
646
+ category: 'accounts',
647
+ title: 'Privileged Account with SPN',
648
+ description: 'Privileged accounts (adminCount=1) have Service Principal Names configured. These accounts are vulnerable to Kerberoasting attacks.',
649
+ count: affected.length,
650
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
651
+ details: affected.length > 0
652
+ ? {
653
+ attackVector: 'Request TGS ticket → Offline crack password → Full admin access',
654
+ recommendation: 'Remove SPNs from admin accounts. Use dedicated service accounts (preferably gMSA) for services.',
655
+ criticalRisk: 'Compromising these accounts grants immediate Domain Admin or equivalent access.',
656
+ }
657
+ : undefined,
658
+ };
659
+ }
660
+ function detectAdminNoSmartcard(users, includeDetails) {
661
+ const affected = users.filter((u) => {
662
+ if (u.adminCount !== 1)
663
+ return false;
664
+ if (!u.enabled)
665
+ return false;
666
+ const smartcardRequired = u.userAccountControl ? (u.userAccountControl & 0x40000) !== 0 : false;
667
+ return !smartcardRequired;
668
+ });
669
+ return {
670
+ type: 'ADMIN_NO_SMARTCARD',
671
+ severity: 'medium',
672
+ category: 'accounts',
673
+ title: 'Admin Account Without Smartcard Requirement',
674
+ description: 'Privileged accounts can authenticate with passwords instead of smartcards. Passwords are more vulnerable to theft and phishing.',
675
+ count: affected.length,
676
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
677
+ details: affected.length > 0
678
+ ? {
679
+ recommendation: 'Enable "Smart card is required for interactive logon" for all admin accounts.',
680
+ benefits: [
681
+ 'Eliminates password-based attacks (phishing, credential theft)',
682
+ 'Provides two-factor authentication',
683
+ 'Reduces risk of credential replay attacks',
684
+ ],
685
+ }
686
+ : undefined,
687
+ };
688
+ }
689
+ function detectServiceAccountInteractive(users, includeDetails) {
690
+ const affected = users.filter((u) => {
691
+ const spn = u['servicePrincipalName'];
692
+ const hasSPN = spn && Array.isArray(spn) && spn.length > 0;
693
+ const servicePatterns = [/^svc[_-]/i, /^sa[_-]/i, /service/i, /^sql/i, /^iis/i, /^app/i];
694
+ const matchesPattern = servicePatterns.some((p) => p.test(u.sAMAccountName || ''));
695
+ if (!hasSPN && !matchesPattern)
696
+ return false;
697
+ if (!u.enabled)
698
+ return false;
699
+ const lastLogonStr = u.lastLogon;
700
+ if (lastLogonStr) {
701
+ const lastLogon = new Date(lastLogonStr);
702
+ const daysSinceLogon = (Date.now() - lastLogon.getTime()) / (1000 * 60 * 60 * 24);
703
+ if (daysSinceLogon < 30) {
704
+ return true;
705
+ }
706
+ }
707
+ const pwdNeverExpires = u.userAccountControl ? (u.userAccountControl & 0x10000) !== 0 : false;
708
+ const notDelegated = u.userAccountControl ? (u.userAccountControl & 0x100000) !== 0 : false;
709
+ return pwdNeverExpires && !notDelegated;
710
+ });
711
+ return {
712
+ type: 'SERVICE_ACCOUNT_INTERACTIVE',
713
+ severity: 'high',
714
+ category: 'accounts',
715
+ title: 'Service Account with Interactive Logon',
716
+ description: 'Service accounts appear to allow or use interactive logon. Service accounts should be restricted to service-only authentication.',
717
+ count: affected.length,
718
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
719
+ details: affected.length > 0
720
+ ? {
721
+ recommendation: 'Apply "Deny log on locally" and "Deny log on through Remote Desktop Services" rights. Use gMSA where possible.',
722
+ risks: [
723
+ 'Interactive sessions leave credentials in memory (mimikatz target)',
724
+ 'Increases attack surface for credential theft',
725
+ 'May indicate misuse of service accounts',
726
+ ],
727
+ }
728
+ : undefined,
729
+ };
730
+ }
731
+ function detectServiceAccountVulnerabilities(users, includeDetails) {
732
+ return [
733
+ detectServiceAccountWithSpn(users, includeDetails),
734
+ detectServiceAccountNaming(users, includeDetails),
735
+ detectServiceAccountOldPassword(users, includeDetails),
736
+ detectServiceAccountPrivileged(users, includeDetails),
737
+ detectServiceAccountNoPreauth(users, includeDetails),
738
+ detectServiceAccountWeakEncryption(users, includeDetails),
739
+ ].filter((finding) => finding.count > 0);
740
+ }
741
+ function detectReplicaDirectoryChanges(users, includeDetails) {
742
+ const replicationGroups = [
743
+ 'Domain Controllers',
744
+ 'Enterprise Domain Controllers',
745
+ 'Administrators',
746
+ 'Domain Admins',
747
+ 'Enterprise Admins',
748
+ ];
749
+ const affected = users.filter((u) => {
750
+ if (!u.enabled || !u.memberOf)
751
+ return false;
752
+ const rawDesc = u['description'];
753
+ const description = typeof rawDesc === 'string' ? rawDesc : (Array.isArray(rawDesc) ? rawDesc[0] : '') || '';
754
+ const hasReplicationHint = description.toLowerCase().includes('replication') ||
755
+ description.toLowerCase().includes('dcsync') ||
756
+ description.toLowerCase().includes('directory sync');
757
+ const isServiceLike = /^(svc|service|sync|repl)/i.test(u.sAMAccountName);
758
+ const hasAdminCount = u.adminCount === 1;
759
+ const isInReplicationGroup = u.memberOf.some((dn) => replicationGroups.some((g) => dn.toLowerCase().includes(g.toLowerCase())));
760
+ return hasReplicationHint || (isServiceLike && hasAdminCount && !isInReplicationGroup);
761
+ });
762
+ return {
763
+ type: 'REPLICA_DIRECTORY_CHANGES',
764
+ severity: 'critical',
765
+ category: 'accounts',
766
+ title: 'Potential Directory Replication Rights',
767
+ description: 'Accounts that may have directory replication rights (DCSync capability). ' +
768
+ 'These accounts can extract all password hashes from the domain.',
769
+ count: affected.length,
770
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
771
+ details: {
772
+ recommendation: 'Review ACLs on domain head for DS-Replication-Get-Changes rights. Only Domain Controllers should have this permission.',
773
+ },
774
+ };
775
+ }
776
+ function detectDangerousBuiltinMembership(users, includeDetails) {
777
+ const dangerousGroups = [
778
+ 'Cert Publishers',
779
+ 'RAS and IAS Servers',
780
+ 'Windows Authorization Access Group',
781
+ 'Terminal Server License Servers',
782
+ 'Incoming Forest Trust Builders',
783
+ 'Performance Log Users',
784
+ 'Performance Monitor Users',
785
+ 'Distributed COM Users',
786
+ 'Remote Desktop Users',
787
+ 'Network Configuration Operators',
788
+ 'Cryptographic Operators',
789
+ 'Event Log Readers',
790
+ 'Hyper-V Administrators',
791
+ 'Access Control Assistance Operators',
792
+ 'Remote Management Users',
793
+ ];
794
+ const affected = users.filter((u) => {
795
+ if (!u.enabled || !u.memberOf)
796
+ return false;
797
+ return u.memberOf.some((dn) => dangerousGroups.some((g) => dn.toLowerCase().includes(g.toLowerCase())));
798
+ });
799
+ return {
800
+ type: 'DANGEROUS_BUILTIN_MEMBERSHIP',
801
+ severity: 'medium',
802
+ category: 'accounts',
803
+ title: 'Dangerous Built-in Group Membership',
804
+ description: 'User accounts with membership in overlooked but dangerous built-in groups. ' +
805
+ 'These groups grant elevated privileges that may allow privilege escalation.',
806
+ count: affected.length,
807
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
808
+ details: {
809
+ dangerousGroups: dangerousGroups,
810
+ },
811
+ };
812
+ }
813
+ function detectLockedAccountAdmin(users, includeDetails) {
814
+ const adminGroups = [
815
+ 'Domain Admins',
816
+ 'Enterprise Admins',
817
+ 'Schema Admins',
818
+ 'Administrators',
819
+ 'Account Operators',
820
+ 'Server Operators',
821
+ 'Backup Operators',
822
+ ];
823
+ const affected = users.filter((u) => {
824
+ if (!u.memberOf)
825
+ return false;
826
+ const isLocked = (u.lockoutTime && u.lockoutTime !== '0' && u.lockoutTime !== 0) ||
827
+ (u.userAccountControl && (u.userAccountControl & 0x10) !== 0);
828
+ const isAdmin = u.memberOf.some((dn) => adminGroups.some((g) => dn.toLowerCase().includes(g.toLowerCase())));
829
+ return isLocked && isAdmin;
830
+ });
831
+ return {
832
+ type: 'LOCKED_ACCOUNT_ADMIN',
833
+ severity: 'high',
834
+ category: 'accounts',
835
+ title: 'Locked Administrative Account',
836
+ description: 'Administrative accounts that are currently locked out. ' +
837
+ 'May indicate password spray attacks or compromised credential attempts.',
838
+ count: affected.length,
839
+ affectedEntities: includeDetails ? (0, entity_converter_1.toAffectedUserEntities)(affected) : undefined,
840
+ details: {
841
+ recommendation: 'Investigate why these admin accounts are locked. Check security logs for failed authentication attempts.',
842
+ },
843
+ };
844
+ }
845
+ function detectAccountsVulnerabilities(users, includeDetails) {
846
+ return [
847
+ detectSensitiveDelegation(users, includeDetails),
848
+ detectDisabledAccountInAdminGroup(users, includeDetails),
849
+ detectExpiredAccountInAdminGroup(users, includeDetails),
850
+ detectSidHistory(users, includeDetails),
851
+ detectNotInProtectedUsers(users, includeDetails),
852
+ detectDomainAdminInDescription(users, includeDetails),
853
+ detectBackupOperatorsMember(users, includeDetails),
854
+ detectAccountOperatorsMember(users, includeDetails),
855
+ detectServerOperatorsMember(users, includeDetails),
856
+ detectPrintOperatorsMember(users, includeDetails),
857
+ detectStaleAccount(users, includeDetails),
858
+ detectInactive365Days(users, includeDetails),
859
+ detectNeverLoggedOn(users, includeDetails),
860
+ detectAccountExpireSoon(users, includeDetails),
861
+ detectAdminLogonCountLow(users, includeDetails),
862
+ detectTestAccount(users, includeDetails),
863
+ detectSharedAccount(users, includeDetails),
864
+ detectSmartcardNotRequired(users, includeDetails),
865
+ detectPrimaryGroupIdSpoofing(users, includeDetails),
866
+ detectServiceAccountWithSpn(users, includeDetails),
867
+ detectServiceAccountNaming(users, includeDetails),
868
+ detectServiceAccountOldPassword(users, includeDetails),
869
+ detectServiceAccountPrivileged(users, includeDetails),
870
+ detectServiceAccountNoPreauth(users, includeDetails),
871
+ detectServiceAccountWeakEncryption(users, includeDetails),
872
+ detectAdminCountOrphaned(users, includeDetails),
873
+ detectPrivilegedAccountSpn(users, includeDetails),
874
+ detectAdminNoSmartcard(users, includeDetails),
875
+ detectServiceAccountInteractive(users, includeDetails),
876
+ detectReplicaDirectoryChanges(users, includeDetails),
877
+ detectDangerousBuiltinMembership(users, includeDetails),
878
+ detectLockedAccountAdmin(users, includeDetails),
879
+ ].filter((finding) => finding.count > 0);
880
+ }
881
+ //# sourceMappingURL=accounts.detector.js.map