@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,1421 @@
1
+ /**
2
+ * Compliance Security Detector
3
+ *
4
+ * Detects security compliance violations based on ANSSI, NIST, CIS, DISA, and
5
+ * industry frameworks (PCI-DSS, SOC2, GDPR, SOX, DORA, HIPAA, ISO27001).
6
+ * Story 1.7: AD Vulnerability Detection Engine - Phase 3 + Compliance Frameworks
7
+ *
8
+ * Vulnerabilities detected (23):
9
+ * - ANSSI_R1_PASSWORD_POLICY (High) - Password policy non-compliant with ANSSI R1
10
+ * - ANSSI_R2_PRIVILEGED_ACCOUNTS (High) - Privileged accounts not compliant with ANSSI R2
11
+ * - ANSSI_R3_STRONG_AUTH (Medium) - Strong authentication not enforced (ANSSI R3)
12
+ * - ANSSI_R4_LOGGING (Medium) - Logging not compliant with ANSSI R4
13
+ * - ANSSI_R5_SEGREGATION (Medium) - Network segregation issues (ANSSI R5)
14
+ * - NIST_AC_2_ACCOUNT_MANAGEMENT (High) - Account management not compliant with NIST AC-2
15
+ * - NIST_AC_6_LEAST_PRIVILEGE (High) - Least privilege violations (NIST AC-6)
16
+ * - NIST_IA_5_AUTHENTICATOR (Medium) - Authenticator management issues (NIST IA-5)
17
+ * - NIST_AU_2_AUDIT_EVENTS (Medium) - Audit events not configured (NIST AU-2)
18
+ * - CIS_PASSWORD_POLICY (High) - Password policy not CIS compliant
19
+ * - CIS_NETWORK_SECURITY (Medium) - Network security settings non-compliant
20
+ * - CIS_USER_RIGHTS (Medium) - User rights assignment issues
21
+ * - DISA_ACCOUNT_POLICIES (High) - Account policies not DISA STIG compliant
22
+ * - DISA_AUDIT_POLICIES (High) - Audit policies not DISA STIG compliant
23
+ * - MFA_NOT_ENFORCED (High) - Privileged accounts without MFA [PCI-DSS, SOC2]
24
+ * - BACKUP_AD_NOT_VERIFIED (High) - No recent AD backup verification [SOC2, DORA]
25
+ * - AUDIT_LOG_RETENTION_SHORT (High) - Log retention below required threshold [SOX, HIPAA]
26
+ * - PRIVILEGED_ACCESS_REVIEW_MISSING (Medium) - No recent access review [SOX, ISO27001]
27
+ * - DATA_CLASSIFICATION_MISSING (Medium) - OUs without data classification [GDPR, ISO27001]
28
+ * - CHANGE_MANAGEMENT_BYPASS (High) - Changes outside approved process [SOX]
29
+ * - VENDOR_ACCOUNT_UNMONITORED (Medium) - Third-party accounts not monitored [DORA]
30
+ * - ENCRYPTION_AT_REST_DISABLED (High) - BitLocker not deployed on DCs [PCI-DSS, HIPAA]
31
+ * - COMPLIANCE_SCORE (Info) - Overall compliance score summary
32
+ */
33
+
34
+ import { ADUser, ADGroup, ADComputer, ADDomain } from '../../../../types/ad.types';
35
+ import { Finding } from '../../../../types/finding.types';
36
+ import { toAffectedUserEntities, ldapAttrToString } from '../../../../utils/entity-converter';
37
+ import { GpoSecuritySettings } from '../../../../providers/smb/smb.provider';
38
+
39
+ /**
40
+ * Password policy interface (from ADDomain)
41
+ */
42
+ interface PasswordPolicy {
43
+ minPwdLength: number;
44
+ pwdHistoryLength: number;
45
+ lockoutThreshold: number;
46
+ lockoutDuration: number;
47
+ maxPwdAge: number;
48
+ minPwdAge: number;
49
+ complexityEnabled: boolean;
50
+ reversibleEncryption: boolean;
51
+ }
52
+
53
+ // ==================== ANSSI COMPLIANCE DETECTORS ====================
54
+
55
+ /**
56
+ * ANSSI R1 - Password Policy Compliance
57
+ * Checks if password policy meets ANSSI recommendations:
58
+ * - Minimum 12 characters for users, 16 for admins
59
+ * - Password history >= 12
60
+ * - Lockout threshold <= 5
61
+ * - Maximum password age <= 90 days
62
+ */
63
+ export function detectAnssiR1PasswordPolicy(
64
+ domain: ADDomain,
65
+ gpoSettings: GpoSecuritySettings | null,
66
+ _includeDetails: boolean
67
+ ): Finding {
68
+ const issues: string[] = [];
69
+ let compliant = true;
70
+
71
+ // Check domain password policy
72
+ const policy = domain['passwordPolicy'] as PasswordPolicy | undefined;
73
+ if (policy) {
74
+ if (policy.minPwdLength < 12) {
75
+ issues.push(`Minimum password length ${policy.minPwdLength} < 12 required`);
76
+ compliant = false;
77
+ }
78
+ if (policy.pwdHistoryLength < 12) {
79
+ issues.push(`Password history ${policy.pwdHistoryLength} < 12 required`);
80
+ compliant = false;
81
+ }
82
+ if (policy.lockoutThreshold > 5 && policy.lockoutThreshold !== 0) {
83
+ issues.push(`Lockout threshold ${policy.lockoutThreshold} > 5 allowed`);
84
+ compliant = false;
85
+ }
86
+ if (policy.maxPwdAge > 90) {
87
+ issues.push(`Max password age ${policy.maxPwdAge} > 90 days`);
88
+ compliant = false;
89
+ }
90
+ } else {
91
+ issues.push('Password policy not configured or not readable');
92
+ compliant = false;
93
+ }
94
+
95
+ // Check GPO settings for fine-grained password policy
96
+ if (gpoSettings?.ldapServerIntegrity !== undefined) {
97
+ // GPO settings available but no password policy in them
98
+ // This is informational only
99
+ }
100
+
101
+ return {
102
+ type: 'ANSSI_R1_PASSWORD_POLICY',
103
+ severity: 'high',
104
+ category: 'compliance',
105
+ title: 'ANSSI R1 - Password Policy Non-Compliant',
106
+ description:
107
+ 'Password policy does not meet ANSSI R1 recommendations. ANSSI requires minimum 12 characters, password history of 12, lockout threshold ≤5, and max age ≤90 days.',
108
+ count: compliant ? 0 : 1,
109
+ details: issues.length > 0 ? { violations: issues, framework: 'ANSSI', control: 'R1' } : undefined,
110
+ };
111
+ }
112
+
113
+ /**
114
+ * ANSSI R2 - Privileged Accounts Management
115
+ * Checks privileged account compliance:
116
+ * - Admin accounts should have separate user accounts
117
+ * - Admin accounts should not have email
118
+ * - Admin accounts should require smartcard
119
+ * - Number of Domain Admins should be limited (<10)
120
+ */
121
+ export function detectAnssiR2PrivilegedAccounts(
122
+ users: ADUser[],
123
+ _groups: ADGroup[],
124
+ includeDetails: boolean
125
+ ): Finding {
126
+ const privilegedGroups = ['Domain Admins', 'Enterprise Admins', 'Schema Admins', 'Administrators'];
127
+ const issues: { user: string; violations: string[] }[] = [];
128
+
129
+ const privilegedUsers = users.filter((u) => {
130
+ if (!u.memberOf) return false;
131
+ return u.memberOf.some((dn) => privilegedGroups.some((pg) => dn.includes(`CN=${pg}`)));
132
+ });
133
+
134
+ // Check each privileged user
135
+ for (const user of privilegedUsers) {
136
+ const userIssues: string[] = [];
137
+
138
+ // Admin account with email configured (should use separate account)
139
+ if (user.mail) {
140
+ userIssues.push('Admin account has email configured (use separate account)');
141
+ }
142
+
143
+ // Admin account without smartcard required
144
+ const uac = user.userAccountControl || 0;
145
+ if ((uac & 0x40000) === 0) {
146
+ // SMARTCARD_REQUIRED flag not set
147
+ userIssues.push('Smartcard not required for privileged account');
148
+ }
149
+
150
+ // Admin account enabled but never logged on
151
+ if (user.enabled && !user.lastLogon) {
152
+ userIssues.push('Enabled admin account never logged on');
153
+ }
154
+
155
+ if (userIssues.length > 0) {
156
+ issues.push({ user: user.sAMAccountName, violations: userIssues });
157
+ }
158
+ }
159
+
160
+ // Check total number of Domain Admins
161
+ const domainAdmins = privilegedUsers.filter((u) =>
162
+ u.memberOf?.some((dn) => dn.includes('CN=Domain Admins'))
163
+ );
164
+ const domainAdminCount = domainAdmins.length;
165
+
166
+ return {
167
+ type: 'ANSSI_R2_PRIVILEGED_ACCOUNTS',
168
+ severity: 'high',
169
+ category: 'compliance',
170
+ title: 'ANSSI R2 - Privileged Accounts Non-Compliant',
171
+ description:
172
+ 'Privileged accounts do not meet ANSSI R2 recommendations. Admin accounts should use separate identities, require smartcard, and not exceed 10 Domain Admins.',
173
+ count: issues.length,
174
+ affectedEntities: includeDetails
175
+ ? toAffectedUserEntities(
176
+ users.filter((u) => issues.some((i) => i.user === u.sAMAccountName))
177
+ )
178
+ : undefined,
179
+ details:
180
+ issues.length > 0
181
+ ? {
182
+ violations: issues.slice(0, 10),
183
+ domainAdminCount,
184
+ recommendation:
185
+ domainAdminCount > 10
186
+ ? `Reduce Domain Admins from ${domainAdminCount} to ≤10`
187
+ : undefined,
188
+ framework: 'ANSSI',
189
+ control: 'R2',
190
+ }
191
+ : undefined,
192
+ };
193
+ }
194
+
195
+ /**
196
+ * ANSSI R3 - Strong Authentication
197
+ * Checks if strong authentication is enforced:
198
+ * - Kerberos AES encryption enabled
199
+ * - NTLM restrictions in place
200
+ * - Credential Guard considerations
201
+ */
202
+ export function detectAnssiR3StrongAuth(
203
+ users: ADUser[],
204
+ domain: ADDomain,
205
+ _includeDetails: boolean
206
+ ): Finding {
207
+ const issues: string[] = [];
208
+
209
+ // Check domain functional level (impacts security features)
210
+ const functionalLevel = (domain['domainFunctionalLevel'] as number) || 0;
211
+ if (functionalLevel < 6) {
212
+ // Windows Server 2012 R2
213
+ issues.push(`Domain functional level ${functionalLevel} is below 2012 R2 (6), limiting security features`);
214
+ }
215
+
216
+ // Check users with weak encryption types
217
+ const weakEncryptionUsers = users.filter((u) => {
218
+ const encTypes = u['msDS-SupportedEncryptionTypes'] as number | undefined;
219
+ if (typeof encTypes !== 'number') return false;
220
+ // Only DES/RC4, no AES
221
+ return (encTypes & 0x18) === 0 && (encTypes & 0x7) !== 0;
222
+ });
223
+
224
+ if (weakEncryptionUsers.length > 0) {
225
+ issues.push(`${weakEncryptionUsers.length} users with weak encryption (no AES)`);
226
+ }
227
+
228
+ // Check for users without Kerberos pre-authentication
229
+ const noPreAuthUsers = users.filter((u) => {
230
+ const uac = u.userAccountControl || 0;
231
+ return (uac & 0x400000) !== 0; // DONT_REQ_PREAUTH
232
+ });
233
+
234
+ if (noPreAuthUsers.length > 0) {
235
+ issues.push(`${noPreAuthUsers.length} users without Kerberos pre-authentication`);
236
+ }
237
+
238
+ return {
239
+ type: 'ANSSI_R3_STRONG_AUTH',
240
+ severity: 'medium',
241
+ category: 'compliance',
242
+ title: 'ANSSI R3 - Strong Authentication Issues',
243
+ description:
244
+ 'Strong authentication mechanisms not fully enforced per ANSSI R3. Kerberos AES should be required, and weak encryption types disabled.',
245
+ count: issues.length > 0 ? 1 : 0,
246
+ details: issues.length > 0 ? { violations: issues, framework: 'ANSSI', control: 'R3' } : undefined,
247
+ };
248
+ }
249
+
250
+ /**
251
+ * ANSSI R4 - Logging and Monitoring
252
+ * Checks logging configuration compliance:
253
+ * - Security event logging enabled
254
+ * - Log size adequate
255
+ * - Retention configured
256
+ */
257
+ export function detectAnssiR4Logging(
258
+ gpoSettings: GpoSecuritySettings | null,
259
+ _includeDetails: boolean
260
+ ): Finding {
261
+ const issues: string[] = [];
262
+
263
+ if (!gpoSettings) {
264
+ issues.push('GPO settings not available for audit policy analysis');
265
+ } else {
266
+ // Check audit policy
267
+ if (gpoSettings.auditPolicies && gpoSettings.auditPolicies.length > 0) {
268
+ const audit = gpoSettings.auditPolicies;
269
+
270
+ // Check essential audit categories
271
+ const hasLogon = audit.some((p) => p.category.includes('Logon') && p.success && p.failure);
272
+ const hasAccountMgmt = audit.some(
273
+ (p) => p.category.includes('Account Management') && p.success && p.failure
274
+ );
275
+ const hasPolicyChange = audit.some(
276
+ (p) => p.category.includes('Policy Change') && p.success && p.failure
277
+ );
278
+ const hasPrivilegeUse = audit.some(
279
+ (p) => p.category.includes('Privilege Use') && p.success && p.failure
280
+ );
281
+
282
+ if (!hasLogon) issues.push('Logon events not fully audited');
283
+ if (!hasAccountMgmt) issues.push('Account management not fully audited');
284
+ if (!hasPolicyChange) issues.push('Policy changes not fully audited');
285
+ if (!hasPrivilegeUse) issues.push('Privilege use not fully audited');
286
+ } else {
287
+ issues.push('Audit policy not configured');
288
+ }
289
+ }
290
+
291
+ return {
292
+ type: 'ANSSI_R4_LOGGING',
293
+ severity: 'medium',
294
+ category: 'compliance',
295
+ title: 'ANSSI R4 - Logging Non-Compliant',
296
+ description:
297
+ 'Logging configuration does not meet ANSSI R4 recommendations. All security events should be audited with adequate log retention.',
298
+ count: issues.length > 0 ? 1 : 0,
299
+ details: issues.length > 0 ? { violations: issues, framework: 'ANSSI', control: 'R4' } : undefined,
300
+ };
301
+ }
302
+
303
+ /**
304
+ * ANSSI R5 - Segregation
305
+ * Checks network and privilege segregation:
306
+ * - Tiered admin model
307
+ * - Service account isolation
308
+ * - Workstation restrictions
309
+ */
310
+ export function detectAnssiR5Segregation(
311
+ users: ADUser[],
312
+ computers: ADComputer[],
313
+ _includeDetails: boolean
314
+ ): Finding {
315
+ const issues: string[] = [];
316
+
317
+ // Check for admin accounts logging into workstations (tier violation)
318
+ const privilegedGroups = ['Domain Admins', 'Enterprise Admins', 'Schema Admins'];
319
+ const admins = users.filter(
320
+ (u) => u.memberOf?.some((dn) => privilegedGroups.some((pg) => dn.includes(`CN=${pg}`)))
321
+ );
322
+
323
+ // Check for service accounts with interactive logon capability
324
+ const serviceAccounts = users.filter((u) => {
325
+ const name = (u.sAMAccountName || '').toLowerCase();
326
+ return name.includes('svc') || name.includes('service') || name.startsWith('sa_');
327
+ });
328
+
329
+ const interactiveServiceAccounts = serviceAccounts.filter((u) => {
330
+ // Check if service account doesn't have "Deny logon locally" applied
331
+ // This is a heuristic - real check would require GPO analysis
332
+ return u.enabled;
333
+ });
334
+
335
+ if (interactiveServiceAccounts.length > 0) {
336
+ issues.push(`${interactiveServiceAccounts.length} service accounts may allow interactive logon`);
337
+ }
338
+
339
+ // Check for workstations in server OUs (organizational issue)
340
+ const workstationsInServerOu = computers.filter((c) => {
341
+ const os = ldapAttrToString(c.operatingSystem);
342
+ const isWorkstation = /windows 10|windows 11|windows 7|windows 8/i.test(os);
343
+ const isInServerOu = /ou=servers|ou=server|ou=datacenter/i.test(c.dn);
344
+ return isWorkstation && isInServerOu;
345
+ });
346
+
347
+ if (workstationsInServerOu.length > 0) {
348
+ issues.push(`${workstationsInServerOu.length} workstations in server OUs (tier violation)`);
349
+ }
350
+
351
+ // Check admin count (too many admins indicates poor segregation)
352
+ if (admins.length > 15) {
353
+ issues.push(`${admins.length} privileged accounts (recommend <15 for proper segregation)`);
354
+ }
355
+
356
+ return {
357
+ type: 'ANSSI_R5_SEGREGATION',
358
+ severity: 'medium',
359
+ category: 'compliance',
360
+ title: 'ANSSI R5 - Segregation Issues',
361
+ description:
362
+ 'Network and privilege segregation does not meet ANSSI R5 recommendations. Implement tiered administration model.',
363
+ count: issues.length > 0 ? 1 : 0,
364
+ details: issues.length > 0 ? { violations: issues, framework: 'ANSSI', control: 'R5' } : undefined,
365
+ };
366
+ }
367
+
368
+ // ==================== NIST COMPLIANCE DETECTORS ====================
369
+
370
+ /**
371
+ * NIST AC-2 - Account Management
372
+ * Checks account management compliance:
373
+ * - Inactive accounts disabled
374
+ * - Guest account disabled
375
+ * - Service accounts documented
376
+ */
377
+ export function detectNistAc2AccountManagement(
378
+ users: ADUser[],
379
+ includeDetails: boolean
380
+ ): Finding {
381
+ const issues: { issue: string; count: number }[] = [];
382
+ const affectedUsers: ADUser[] = [];
383
+
384
+ // Check for inactive accounts (90 days)
385
+ const now = Date.now();
386
+ const ninetyDaysAgo = now - 90 * 24 * 60 * 60 * 1000;
387
+ const inactiveAccounts = users.filter(
388
+ (u) => u.enabled && u.lastLogon && u.lastLogon.getTime() < ninetyDaysAgo
389
+ );
390
+ if (inactiveAccounts.length > 0) {
391
+ issues.push({ issue: 'Inactive accounts (90+ days) still enabled', count: inactiveAccounts.length });
392
+ affectedUsers.push(...inactiveAccounts);
393
+ }
394
+
395
+ // Check for enabled accounts that have never logged on
396
+ const neverLoggedOn = users.filter((u) => u.enabled && !u.lastLogon);
397
+ if (neverLoggedOn.length > 0) {
398
+ issues.push({ issue: 'Enabled accounts never logged on', count: neverLoggedOn.length });
399
+ affectedUsers.push(...neverLoggedOn.filter((u) => !affectedUsers.includes(u)));
400
+ }
401
+
402
+ // Check for Guest account enabled
403
+ const guestEnabled = users.filter(
404
+ (u) => u.sAMAccountName.toLowerCase() === 'guest' && u.enabled
405
+ );
406
+ if (guestEnabled.length > 0) {
407
+ issues.push({ issue: 'Guest account enabled', count: 1 });
408
+ affectedUsers.push(...guestEnabled);
409
+ }
410
+
411
+ return {
412
+ type: 'NIST_AC_2_ACCOUNT_MANAGEMENT',
413
+ severity: 'high',
414
+ category: 'compliance',
415
+ title: 'NIST AC-2 - Account Management Issues',
416
+ description:
417
+ 'Account management does not comply with NIST AC-2. Inactive accounts should be disabled, guest account should be disabled.',
418
+ count: affectedUsers.length,
419
+ affectedEntities: includeDetails ? toAffectedUserEntities(affectedUsers.slice(0, 50)) : undefined,
420
+ details: issues.length > 0 ? { violations: issues, framework: 'NIST', control: 'AC-2' } : undefined,
421
+ };
422
+ }
423
+
424
+ /**
425
+ * NIST AC-6 - Least Privilege
426
+ * Checks least privilege compliance:
427
+ * - Users with unnecessary admin rights
428
+ * - Excessive group memberships
429
+ * - Accounts with elevated privileges
430
+ */
431
+ export function detectNistAc6LeastPrivilege(
432
+ users: ADUser[],
433
+ groups: ADGroup[],
434
+ includeDetails: boolean
435
+ ): Finding {
436
+ const issues: { issue: string; count: number }[] = [];
437
+ const affectedUsers: ADUser[] = [];
438
+
439
+ const sensitiveGroups = [
440
+ 'Domain Admins',
441
+ 'Enterprise Admins',
442
+ 'Schema Admins',
443
+ 'Administrators',
444
+ 'Account Operators',
445
+ 'Backup Operators',
446
+ 'Server Operators',
447
+ 'Print Operators',
448
+ ];
449
+
450
+ // Check users in multiple sensitive groups
451
+ const usersInMultipleSensitiveGroups = users.filter((u) => {
452
+ if (!u.memberOf) return false;
453
+ const sensitiveCount = u.memberOf.filter((dn) =>
454
+ sensitiveGroups.some((sg) => dn.includes(`CN=${sg}`))
455
+ ).length;
456
+ return sensitiveCount > 1;
457
+ });
458
+
459
+ if (usersInMultipleSensitiveGroups.length > 0) {
460
+ issues.push({
461
+ issue: 'Users in multiple sensitive groups',
462
+ count: usersInMultipleSensitiveGroups.length,
463
+ });
464
+ affectedUsers.push(...usersInMultipleSensitiveGroups);
465
+ }
466
+
467
+ // Check for excessive Domain Admins (>5)
468
+ const domainAdmins = users.filter(
469
+ (u) => u.memberOf?.some((dn) => dn.includes('CN=Domain Admins'))
470
+ );
471
+ if (domainAdmins.length > 5) {
472
+ issues.push({
473
+ issue: `Excessive Domain Admins (${domainAdmins.length}, recommend ≤5)`,
474
+ count: domainAdmins.length,
475
+ });
476
+ }
477
+
478
+ // Check for groups with excessive members (privilege creep indicator)
479
+ const oversizedPrivilegedGroups = groups.filter((g) => {
480
+ const isSensitive = sensitiveGroups.some(
481
+ (sg) => g.sAMAccountName?.toLowerCase() === sg.toLowerCase()
482
+ );
483
+ return isSensitive && (g.member?.length || 0) > 10;
484
+ });
485
+
486
+ if (oversizedPrivilegedGroups.length > 0) {
487
+ issues.push({
488
+ issue: 'Privileged groups with >10 members',
489
+ count: oversizedPrivilegedGroups.length,
490
+ });
491
+ }
492
+
493
+ return {
494
+ type: 'NIST_AC_6_LEAST_PRIVILEGE',
495
+ severity: 'high',
496
+ category: 'compliance',
497
+ title: 'NIST AC-6 - Least Privilege Violations',
498
+ description:
499
+ 'Privilege assignments do not comply with NIST AC-6 least privilege principle. Review and reduce excessive privileges.',
500
+ count: affectedUsers.length + oversizedPrivilegedGroups.length,
501
+ affectedEntities: includeDetails ? toAffectedUserEntities(affectedUsers.slice(0, 50)) : undefined,
502
+ details: issues.length > 0 ? { violations: issues, framework: 'NIST', control: 'AC-6' } : undefined,
503
+ };
504
+ }
505
+
506
+ /**
507
+ * NIST IA-5 - Authenticator Management
508
+ * Checks authenticator compliance:
509
+ * - Password complexity
510
+ * - Account lockout
511
+ * - Password age
512
+ */
513
+ export function detectNistIa5Authenticator(
514
+ domain: ADDomain,
515
+ users: ADUser[],
516
+ _includeDetails: boolean
517
+ ): Finding {
518
+ const issues: string[] = [];
519
+
520
+ // Check password policy
521
+ const policy = domain['passwordPolicy'] as PasswordPolicy | undefined;
522
+ if (policy) {
523
+ if (!policy.complexityEnabled) {
524
+ issues.push('Password complexity not enabled');
525
+ }
526
+ if (policy.minPwdLength < 14) {
527
+ issues.push(`Minimum password length ${policy.minPwdLength} < 14 (NIST recommends 14+)`);
528
+ }
529
+ if (policy.lockoutThreshold === 0) {
530
+ issues.push('Account lockout not configured');
531
+ }
532
+ }
533
+
534
+ // Check for accounts with password not required
535
+ const noPasswordRequired = users.filter((u) => {
536
+ const uac = u.userAccountControl || 0;
537
+ return (uac & 0x20) !== 0; // PASSWD_NOTREQD
538
+ });
539
+
540
+ if (noPasswordRequired.length > 0) {
541
+ issues.push(`${noPasswordRequired.length} accounts with password not required`);
542
+ }
543
+
544
+ // Check for reversible encryption
545
+ const reversibleEncryption = users.filter((u) => {
546
+ const uac = u.userAccountControl || 0;
547
+ return (uac & 0x80) !== 0; // ENCRYPTED_TEXT_PWD_ALLOWED
548
+ });
549
+
550
+ if (reversibleEncryption.length > 0) {
551
+ issues.push(`${reversibleEncryption.length} accounts with reversible encryption`);
552
+ }
553
+
554
+ return {
555
+ type: 'NIST_IA_5_AUTHENTICATOR',
556
+ severity: 'medium',
557
+ category: 'compliance',
558
+ title: 'NIST IA-5 - Authenticator Management Issues',
559
+ description:
560
+ 'Authenticator management does not comply with NIST IA-5. Password policies should enforce complexity and secure storage.',
561
+ count: issues.length > 0 ? 1 : 0,
562
+ details: issues.length > 0 ? { violations: issues, framework: 'NIST', control: 'IA-5' } : undefined,
563
+ };
564
+ }
565
+
566
+ /**
567
+ * NIST AU-2 - Audit Events
568
+ * Checks audit event configuration:
569
+ * - Essential events audited
570
+ * - Audit policy completeness
571
+ */
572
+ export function detectNistAu2AuditEvents(
573
+ gpoSettings: GpoSecuritySettings | null,
574
+ _includeDetails: boolean
575
+ ): Finding {
576
+ const issues: string[] = [];
577
+
578
+ if (!gpoSettings?.auditPolicies || gpoSettings.auditPolicies.length === 0) {
579
+ issues.push('Audit policy not configured or not readable');
580
+ } else {
581
+ const audit = gpoSettings.auditPolicies;
582
+
583
+ // NIST AU-2 required events
584
+ const requiredCategories = [
585
+ 'Account Logon',
586
+ 'Account Management',
587
+ 'Policy Change',
588
+ 'System',
589
+ 'Object Access',
590
+ ];
591
+
592
+ for (const category of requiredCategories) {
593
+ const hasCategory = audit.some(
594
+ (p) => p.category.includes(category) && p.success && p.failure
595
+ );
596
+ if (!hasCategory) {
597
+ issues.push(`${category} not fully audited (success and failure)`);
598
+ }
599
+ }
600
+ }
601
+
602
+ return {
603
+ type: 'NIST_AU_2_AUDIT_EVENTS',
604
+ severity: 'medium',
605
+ category: 'compliance',
606
+ title: 'NIST AU-2 - Audit Events Non-Compliant',
607
+ description:
608
+ 'Audit event configuration does not comply with NIST AU-2. All security-relevant events should be audited.',
609
+ count: issues.length > 0 ? 1 : 0,
610
+ details: issues.length > 0 ? { violations: issues, framework: 'NIST', control: 'AU-2' } : undefined,
611
+ };
612
+ }
613
+
614
+ // ==================== CIS COMPLIANCE DETECTORS ====================
615
+
616
+ /**
617
+ * CIS Password Policy (2.3.1.x)
618
+ * Checks CIS Benchmark password policy recommendations
619
+ */
620
+ export function detectCisPasswordPolicy(
621
+ domain: ADDomain,
622
+ _includeDetails: boolean
623
+ ): Finding {
624
+ const issues: string[] = [];
625
+
626
+ const policy = domain['passwordPolicy'] as PasswordPolicy | undefined;
627
+ if (policy) {
628
+ // CIS recommends minimum 14 characters
629
+ if (policy.minPwdLength < 14) {
630
+ issues.push(`Minimum password length ${policy.minPwdLength} < 14 (CIS 1.1.1)`);
631
+ }
632
+ // CIS recommends password history of 24
633
+ if (policy.pwdHistoryLength < 24) {
634
+ issues.push(`Password history ${policy.pwdHistoryLength} < 24 (CIS 1.1.2)`);
635
+ }
636
+ // CIS recommends max password age 365 days or less
637
+ if (policy.maxPwdAge > 365) {
638
+ issues.push(`Max password age ${policy.maxPwdAge} > 365 days (CIS 1.1.3)`);
639
+ }
640
+ // CIS recommends minimum password age of 1 day
641
+ if (policy.minPwdAge < 1) {
642
+ issues.push(`Min password age ${policy.minPwdAge} < 1 day (CIS 1.1.4)`);
643
+ }
644
+ // Complexity should be enabled
645
+ if (!policy.complexityEnabled) {
646
+ issues.push('Password complexity not enabled (CIS 1.1.5)');
647
+ }
648
+ // Reversible encryption should be disabled
649
+ if (policy.reversibleEncryption) {
650
+ issues.push('Reversible encryption enabled (CIS 1.1.6)');
651
+ }
652
+ } else {
653
+ issues.push('Password policy not available');
654
+ }
655
+
656
+ return {
657
+ type: 'CIS_PASSWORD_POLICY',
658
+ severity: 'high',
659
+ category: 'compliance',
660
+ title: 'CIS Benchmark - Password Policy Non-Compliant',
661
+ description:
662
+ 'Password policy does not meet CIS Benchmark recommendations. Review and update password policy settings.',
663
+ count: issues.length > 0 ? 1 : 0,
664
+ details: issues.length > 0 ? { violations: issues, framework: 'CIS', control: '1.1.x' } : undefined,
665
+ };
666
+ }
667
+
668
+ /**
669
+ * CIS Network Security (2.3.7.x)
670
+ * Checks CIS Benchmark network security settings
671
+ */
672
+ export function detectCisNetworkSecurity(
673
+ gpoSettings: GpoSecuritySettings | null,
674
+ _includeDetails: boolean
675
+ ): Finding {
676
+ const issues: string[] = [];
677
+
678
+ if (gpoSettings) {
679
+ // Check SMBv1
680
+ if (gpoSettings.smbv1ServerEnabled || gpoSettings.smbv1ClientEnabled) {
681
+ issues.push('SMBv1 enabled (CIS 2.3.7.1 - Disable SMBv1)');
682
+ }
683
+
684
+ // Check LDAP signing
685
+ if (gpoSettings.ldapServerIntegrity !== undefined && gpoSettings.ldapServerIntegrity < 2) {
686
+ issues.push('LDAP signing not required (CIS 2.3.7.2)');
687
+ }
688
+
689
+ // Check LDAP channel binding
690
+ if (gpoSettings.ldapChannelBinding !== undefined && gpoSettings.ldapChannelBinding < 2) {
691
+ issues.push('LDAP channel binding not required (CIS 2.3.7.3)');
692
+ }
693
+ } else {
694
+ issues.push('GPO settings not available for network security analysis');
695
+ }
696
+
697
+ return {
698
+ type: 'CIS_NETWORK_SECURITY',
699
+ severity: 'medium',
700
+ category: 'compliance',
701
+ title: 'CIS Benchmark - Network Security Non-Compliant',
702
+ description:
703
+ 'Network security settings do not meet CIS Benchmark recommendations. SMBv1 should be disabled, LDAP signing required.',
704
+ count: issues.length > 0 ? 1 : 0,
705
+ details: issues.length > 0 ? { violations: issues, framework: 'CIS', control: '2.3.7.x' } : undefined,
706
+ };
707
+ }
708
+
709
+ /**
710
+ * CIS User Rights (2.3.11.x)
711
+ * Checks CIS Benchmark user rights assignments
712
+ */
713
+ export function detectCisUserRights(
714
+ users: ADUser[],
715
+ groups: ADGroup[],
716
+ _includeDetails: boolean
717
+ ): Finding {
718
+ const issues: string[] = [];
719
+
720
+ // Check for Everyone or Authenticated Users in privileged groups
721
+ const sensitiveGroups = ['Administrators', 'Domain Admins', 'Enterprise Admins'];
722
+ const problematicMembers = ['Everyone', 'Authenticated Users', 'ANONYMOUS LOGON'];
723
+
724
+ for (const group of groups) {
725
+ const isSensitive = sensitiveGroups.some(
726
+ (sg) => group.sAMAccountName?.toLowerCase() === sg.toLowerCase()
727
+ );
728
+ if (!isSensitive) continue;
729
+
730
+ const hasProblematicMember = group.member?.some((memberDn) =>
731
+ problematicMembers.some((pm) => memberDn.toLowerCase().includes(pm.toLowerCase()))
732
+ );
733
+
734
+ if (hasProblematicMember) {
735
+ issues.push(`${group.sAMAccountName} contains well-known security principal (CIS 2.3.11.x)`);
736
+ }
737
+ }
738
+
739
+ // Check for accounts with "Act as part of the operating system" potential
740
+ const trustedForDelegation = users.filter((u) => {
741
+ const uac = u.userAccountControl || 0;
742
+ return (uac & 0x80000) !== 0; // TRUSTED_FOR_DELEGATION
743
+ });
744
+
745
+ if (trustedForDelegation.length > 0) {
746
+ issues.push(`${trustedForDelegation.length} users trusted for delegation (CIS 2.3.11.x)`);
747
+ }
748
+
749
+ return {
750
+ type: 'CIS_USER_RIGHTS',
751
+ severity: 'medium',
752
+ category: 'compliance',
753
+ title: 'CIS Benchmark - User Rights Issues',
754
+ description:
755
+ 'User rights assignments do not meet CIS Benchmark recommendations. Review delegation and group memberships.',
756
+ count: issues.length > 0 ? 1 : 0,
757
+ details: issues.length > 0 ? { violations: issues, framework: 'CIS', control: '2.3.11.x' } : undefined,
758
+ };
759
+ }
760
+
761
+ // ==================== DISA STIG COMPLIANCE DETECTORS ====================
762
+
763
+ /**
764
+ * DISA STIG V-220857 - Account Policies
765
+ * Checks DISA STIG account policy requirements
766
+ */
767
+ export function detectDisaAccountPolicies(
768
+ domain: ADDomain,
769
+ users: ADUser[],
770
+ _includeDetails: boolean
771
+ ): Finding {
772
+ const issues: string[] = [];
773
+
774
+ const policy = domain['passwordPolicy'] as PasswordPolicy | undefined;
775
+ if (policy) {
776
+ // DISA requires minimum 15 characters for privileged accounts
777
+ if (policy.minPwdLength < 15) {
778
+ issues.push(`Min password length ${policy.minPwdLength} < 15 (V-220857)`);
779
+ }
780
+ // Lockout duration must be 0 (until admin unlock) or >= 15 minutes
781
+ if (policy.lockoutDuration > 0 && policy.lockoutDuration < 15) {
782
+ issues.push(`Lockout duration ${policy.lockoutDuration} min < 15 min (V-220857)`);
783
+ }
784
+ // Lockout threshold must be <= 3
785
+ if (policy.lockoutThreshold > 3) {
786
+ issues.push(`Lockout threshold ${policy.lockoutThreshold} > 3 (V-220857)`);
787
+ }
788
+ }
789
+
790
+ // Check for accounts without password expiration (except service accounts)
791
+ const noExpiration = users.filter((u) => {
792
+ const uac = u.userAccountControl || 0;
793
+ const hasNoExpire = (uac & 0x10000) !== 0; // DONT_EXPIRE_PASSWORD
794
+ const isServiceAccount =
795
+ u.sAMAccountName.toLowerCase().includes('svc') ||
796
+ u.sAMAccountName.toLowerCase().includes('service');
797
+ return hasNoExpire && !isServiceAccount;
798
+ });
799
+
800
+ if (noExpiration.length > 0) {
801
+ issues.push(`${noExpiration.length} non-service accounts with password never expires (V-220857)`);
802
+ }
803
+
804
+ return {
805
+ type: 'DISA_ACCOUNT_POLICIES',
806
+ severity: 'high',
807
+ category: 'compliance',
808
+ title: 'DISA STIG - Account Policies Non-Compliant',
809
+ description:
810
+ 'Account policies do not comply with DISA STIG V-220857. Review password and lockout policy settings.',
811
+ count: issues.length > 0 ? 1 : 0,
812
+ details: issues.length > 0 ? { violations: issues, framework: 'DISA', control: 'V-220857' } : undefined,
813
+ };
814
+ }
815
+
816
+ /**
817
+ * DISA STIG V-220858 - Audit Policies
818
+ * Checks DISA STIG audit policy requirements
819
+ */
820
+ export function detectDisaAuditPolicies(
821
+ gpoSettings: GpoSecuritySettings | null,
822
+ _includeDetails: boolean
823
+ ): Finding {
824
+ const issues: string[] = [];
825
+
826
+ if (!gpoSettings?.auditPolicies || gpoSettings.auditPolicies.length === 0) {
827
+ issues.push('Audit policy not configured (V-220858)');
828
+ } else {
829
+ const audit = gpoSettings.auditPolicies;
830
+
831
+ // DISA requires specific audit categories
832
+ const disaRequirements = [
833
+ { name: 'Account Logon (Success)', check: (a: typeof audit) => a.some((p) => p.category.includes('Logon') && p.success) },
834
+ { name: 'Account Logon (Failure)', check: (a: typeof audit) => a.some((p) => p.category.includes('Logon') && p.failure) },
835
+ { name: 'Account Management (Success)', check: (a: typeof audit) => a.some((p) => p.category.includes('Account Management') && p.success) },
836
+ { name: 'Account Management (Failure)', check: (a: typeof audit) => a.some((p) => p.category.includes('Account Management') && p.failure) },
837
+ { name: 'Policy Change (Success)', check: (a: typeof audit) => a.some((p) => p.category.includes('Policy Change') && p.success) },
838
+ { name: 'Policy Change (Failure)', check: (a: typeof audit) => a.some((p) => p.category.includes('Policy Change') && p.failure) },
839
+ { name: 'Privilege Use (Success)', check: (a: typeof audit) => a.some((p) => p.category.includes('Privilege Use') && p.success) },
840
+ { name: 'Privilege Use (Failure)', check: (a: typeof audit) => a.some((p) => p.category.includes('Privilege Use') && p.failure) },
841
+ ];
842
+
843
+ for (const req of disaRequirements) {
844
+ if (!req.check(audit)) {
845
+ issues.push(`${req.name} audit not enabled (V-220858)`);
846
+ }
847
+ }
848
+ }
849
+
850
+ return {
851
+ type: 'DISA_AUDIT_POLICIES',
852
+ severity: 'high',
853
+ category: 'compliance',
854
+ title: 'DISA STIG - Audit Policies Non-Compliant',
855
+ description:
856
+ 'Audit policies do not comply with DISA STIG V-220858. All required audit categories should be enabled.',
857
+ count: issues.length > 0 ? 1 : 0,
858
+ details: issues.length > 0 ? { violations: issues, framework: 'DISA', control: 'V-220858' } : undefined,
859
+ };
860
+ }
861
+
862
+ // ==================== INDUSTRY FRAMEWORKS (PCI-DSS, SOC2, GDPR, SOX, DORA, HIPAA, ISO27001) ====================
863
+
864
+ /**
865
+ * MFA_NOT_ENFORCED - Privileged accounts without MFA
866
+ * Frameworks: PCI-DSS 8.3, SOC2 CC6.1, ISO27001 A.9.4.2
867
+ * Checks if privileged accounts require smartcard/MFA
868
+ */
869
+ export function detectMfaNotEnforced(
870
+ users: ADUser[],
871
+ includeDetails: boolean
872
+ ): Finding {
873
+ const privilegedGroups = ['Domain Admins', 'Enterprise Admins', 'Schema Admins', 'Administrators'];
874
+
875
+ const privilegedUsers = users.filter((u) => {
876
+ if (!u.memberOf) return false;
877
+ return u.memberOf.some((dn) => privilegedGroups.some((pg) => dn.includes(`CN=${pg}`)));
878
+ });
879
+
880
+ // Check for privileged users without SMARTCARD_REQUIRED flag
881
+ const noMfa = privilegedUsers.filter((u) => {
882
+ const uac = u.userAccountControl || 0;
883
+ return (uac & 0x40000) === 0; // SMARTCARD_REQUIRED not set
884
+ });
885
+
886
+ return {
887
+ type: 'MFA_NOT_ENFORCED',
888
+ severity: 'high',
889
+ category: 'compliance',
890
+ title: 'MFA Not Enforced for Privileged Accounts',
891
+ description:
892
+ 'Privileged accounts do not require multi-factor authentication (smartcard). Required by PCI-DSS 8.3, SOC2 CC6.1, ISO27001 A.9.4.2.',
893
+ count: noMfa.length,
894
+ affectedEntities: includeDetails ? toAffectedUserEntities(noMfa.slice(0, 50)) : undefined,
895
+ details: noMfa.length > 0 ? {
896
+ frameworks: ['PCI-DSS', 'SOC2', 'ISO27001'],
897
+ controls: ['8.3', 'CC6.1', 'A.9.4.2'],
898
+ recommendation: 'Enable smartcard requirement for all privileged accounts',
899
+ } : undefined,
900
+ };
901
+ }
902
+
903
+ /**
904
+ * BACKUP_AD_NOT_VERIFIED - No recent AD backup
905
+ * Frameworks: SOC2 A1.2, DORA Art.11, ISO27001 A.12.3.1
906
+ * Checks if AD has recent backup (based on tombstone lifetime and domain metadata)
907
+ */
908
+ export function detectBackupNotVerified(
909
+ domain: ADDomain,
910
+ _includeDetails: boolean
911
+ ): Finding {
912
+ const issues: string[] = [];
913
+
914
+ // Check tombstone lifetime (default 180 days, should be configured)
915
+ const tombstoneLifetime = (domain['tombstoneLifetime'] as number) || 180;
916
+
917
+ // If tombstone is default, backup policy may not be reviewed
918
+ if (tombstoneLifetime === 180 || tombstoneLifetime === 60) {
919
+ issues.push(`Tombstone lifetime is default (${tombstoneLifetime} days) - backup policy may not be configured`);
920
+ }
921
+
922
+ // Check for backup indicators in domain (this is heuristic)
923
+ // Real backup verification requires checking Windows Server Backup or third-party tools
924
+ const lastBackup = domain['lastBackupTime'] as Date | undefined;
925
+ if (!lastBackup) {
926
+ issues.push('No backup metadata found - verify AD backup is configured and tested');
927
+ }
928
+
929
+ return {
930
+ type: 'BACKUP_AD_NOT_VERIFIED',
931
+ severity: 'high',
932
+ category: 'compliance',
933
+ title: 'AD Backup Not Verified',
934
+ description:
935
+ 'Active Directory backup configuration cannot be verified. Required by SOC2 A1.2, DORA Article 11, ISO27001 A.12.3.1.',
936
+ count: issues.length > 0 ? 1 : 0,
937
+ details: issues.length > 0 ? {
938
+ violations: issues,
939
+ frameworks: ['SOC2', 'DORA', 'ISO27001'],
940
+ controls: ['A1.2', 'Art.11', 'A.12.3.1'],
941
+ recommendation: 'Configure and regularly test AD system state backups',
942
+ } : undefined,
943
+ };
944
+ }
945
+
946
+ /**
947
+ * AUDIT_LOG_RETENTION_SHORT - Log retention below requirements
948
+ * Frameworks: SOX Section 802, HIPAA 164.312(b), PCI-DSS 10.7
949
+ * Checks if audit log retention meets compliance requirements (1 year minimum)
950
+ */
951
+ export function detectAuditLogRetentionShort(
952
+ gpoSettings: GpoSecuritySettings | null,
953
+ _includeDetails: boolean
954
+ ): Finding {
955
+ const issues: string[] = [];
956
+
957
+ // Check if audit policy is configured (indicates logging is set up)
958
+ if (!gpoSettings?.auditPolicies || gpoSettings.auditPolicies.length === 0) {
959
+ issues.push('Audit policy not configured - log retention cannot be verified');
960
+ } else {
961
+ // Check if essential events are being audited (prerequisite for retention)
962
+ const hasSecurityAudit = gpoSettings.auditPolicies.some(
963
+ (p) => p.category.includes('Logon') || p.category.includes('Account')
964
+ );
965
+ if (!hasSecurityAudit) {
966
+ issues.push('Security events not being audited - retention policy meaningless without logging');
967
+ }
968
+ }
969
+
970
+ // Note: Actual log retention settings are in Windows Event Log configuration
971
+ // which requires additional GPO parsing or WMI queries not currently available
972
+ // This check ensures audit infrastructure exists as prerequisite
973
+ if (issues.length === 0) {
974
+ issues.push('Log retention period should be verified manually (1 year minimum for compliance)');
975
+ }
976
+
977
+ return {
978
+ type: 'AUDIT_LOG_RETENTION_SHORT',
979
+ severity: 'high',
980
+ category: 'compliance',
981
+ title: 'Audit Log Retention Below Requirements',
982
+ description:
983
+ 'Audit log retention period may not meet compliance requirements (1 year minimum). Required by SOX Section 802, HIPAA 164.312(b), PCI-DSS 10.7.',
984
+ count: issues.length > 0 ? 1 : 0,
985
+ details: issues.length > 0 ? {
986
+ violations: issues,
987
+ frameworks: ['SOX', 'HIPAA', 'PCI-DSS'],
988
+ controls: ['Section 802', '164.312(b)', '10.7'],
989
+ recommendation: 'Configure log retention for minimum 1 year with SIEM integration',
990
+ } : undefined,
991
+ };
992
+ }
993
+
994
+ /**
995
+ * PRIVILEGED_ACCESS_REVIEW_MISSING - No recent access review
996
+ * Frameworks: SOX Section 404, ISO27001 A.9.2.5, SOC2 CC6.2
997
+ * Checks if privileged group membership has been reviewed recently
998
+ */
999
+ export function detectPrivilegedAccessReviewMissing(
1000
+ users: ADUser[],
1001
+ groups: ADGroup[],
1002
+ _includeDetails: boolean
1003
+ ): Finding {
1004
+ const issues: string[] = [];
1005
+ const NINETY_DAYS_MS = 90 * 24 * 60 * 60 * 1000;
1006
+ const now = Date.now();
1007
+
1008
+ const privilegedGroupNames = ['Domain Admins', 'Enterprise Admins', 'Schema Admins', 'Administrators'];
1009
+
1010
+ // Check privileged groups for stale membership
1011
+ for (const group of groups) {
1012
+ if (!privilegedGroupNames.some(pg => group.sAMAccountName?.toLowerCase() === pg.toLowerCase())) {
1013
+ continue;
1014
+ }
1015
+
1016
+ // Check whenChanged on group (indicates membership review)
1017
+ const groupChanged = group['whenChanged'] as Date | undefined;
1018
+ if (groupChanged && (now - groupChanged.getTime()) > NINETY_DAYS_MS) {
1019
+ // Group not modified in 90 days - may indicate no access review
1020
+ const memberCount = group.member?.length || 0;
1021
+ if (memberCount > 0) {
1022
+ issues.push(`${group.sAMAccountName} (${memberCount} members) not reviewed in 90+ days`);
1023
+ }
1024
+ }
1025
+ }
1026
+
1027
+ // Check for admin accounts with very old last logon (may be orphaned)
1028
+ const privilegedUsers = users.filter((u) => {
1029
+ if (!u.memberOf) return false;
1030
+ return u.memberOf.some((dn) => privilegedGroupNames.some((pg) => dn.includes(`CN=${pg}`)));
1031
+ });
1032
+
1033
+ const staleAdmins = privilegedUsers.filter((u) => {
1034
+ if (!u.lastLogon) return true; // Never logged on
1035
+ return (now - u.lastLogon.getTime()) > NINETY_DAYS_MS;
1036
+ });
1037
+
1038
+ if (staleAdmins.length > 0) {
1039
+ issues.push(`${staleAdmins.length} privileged accounts inactive for 90+ days`);
1040
+ }
1041
+
1042
+ return {
1043
+ type: 'PRIVILEGED_ACCESS_REVIEW_MISSING',
1044
+ severity: 'medium',
1045
+ category: 'compliance',
1046
+ title: 'Privileged Access Review Missing',
1047
+ description:
1048
+ 'Privileged access has not been reviewed recently. Required by SOX Section 404, ISO27001 A.9.2.5, SOC2 CC6.2.',
1049
+ count: issues.length > 0 ? 1 : 0,
1050
+ details: issues.length > 0 ? {
1051
+ violations: issues,
1052
+ frameworks: ['SOX', 'ISO27001', 'SOC2'],
1053
+ controls: ['Section 404', 'A.9.2.5', 'CC6.2'],
1054
+ recommendation: 'Implement quarterly privileged access reviews with documented approval',
1055
+ } : undefined,
1056
+ };
1057
+ }
1058
+
1059
+ /**
1060
+ * DATA_CLASSIFICATION_MISSING - OUs without data classification
1061
+ * Frameworks: GDPR Art.30, ISO27001 A.8.2.1, HIPAA 164.312
1062
+ * Checks if organizational structure supports data classification
1063
+ */
1064
+ export function detectDataClassificationMissing(
1065
+ domain: ADDomain,
1066
+ _includeDetails: boolean
1067
+ ): Finding {
1068
+ const issues: string[] = [];
1069
+
1070
+ // Check domain description for classification policy
1071
+ const domainDescription = (domain['description'] as string) || '';
1072
+ const hasClassificationKeywords = /confidential|restricted|internal|public|sensitive|pii|phi|pci/i.test(domainDescription);
1073
+
1074
+ if (!hasClassificationKeywords) {
1075
+ issues.push('Domain description does not indicate data classification policy');
1076
+ }
1077
+
1078
+ // Check for classification-related attributes in schema (heuristic)
1079
+ // Real check would involve examining OU descriptions and custom attributes
1080
+ const msExchVersion = domain['msExchVersion'] as number | undefined;
1081
+ if (msExchVersion) {
1082
+ // Exchange present - likely has email data requiring classification
1083
+ issues.push('Exchange detected - email data classification policy required for GDPR/HIPAA');
1084
+ }
1085
+
1086
+ return {
1087
+ type: 'DATA_CLASSIFICATION_MISSING',
1088
+ severity: 'medium',
1089
+ category: 'compliance',
1090
+ title: 'Data Classification Not Implemented',
1091
+ description:
1092
+ 'Data classification scheme not detected in AD structure. Required by GDPR Article 30, ISO27001 A.8.2.1, HIPAA 164.312.',
1093
+ count: issues.length > 0 ? 1 : 0,
1094
+ details: issues.length > 0 ? {
1095
+ violations: issues,
1096
+ frameworks: ['GDPR', 'ISO27001', 'HIPAA'],
1097
+ controls: ['Art.30', 'A.8.2.1', '164.312'],
1098
+ recommendation: 'Implement data classification scheme using OU structure and object attributes',
1099
+ } : undefined,
1100
+ };
1101
+ }
1102
+
1103
+ /**
1104
+ * CHANGE_MANAGEMENT_BYPASS - Changes outside approved process
1105
+ * Frameworks: SOX Section 404, ISO27001 A.12.1.2, SOC2 CC8.1
1106
+ * Detects admin changes that may bypass change management
1107
+ */
1108
+ export function detectChangeManagementBypass(
1109
+ users: ADUser[],
1110
+ groups: ADGroup[],
1111
+ _includeDetails: boolean
1112
+ ): Finding {
1113
+ const issues: string[] = [];
1114
+ const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
1115
+ const now = Date.now();
1116
+
1117
+ const privilegedGroupNames = ['Domain Admins', 'Enterprise Admins', 'Schema Admins'];
1118
+
1119
+ // Check for recent privileged group changes (potential bypass indicators)
1120
+ for (const group of groups) {
1121
+ if (!privilegedGroupNames.some(pg => group.sAMAccountName?.toLowerCase() === pg.toLowerCase())) {
1122
+ continue;
1123
+ }
1124
+
1125
+ const groupChanged = group['whenChanged'] as Date | undefined;
1126
+ if (groupChanged && (now - groupChanged.getTime()) < SEVEN_DAYS_MS) {
1127
+ issues.push(`${group.sAMAccountName} modified in last 7 days - verify change request exists`);
1128
+ }
1129
+ }
1130
+
1131
+ // Check for recently created admin accounts
1132
+ const recentAdmins = users.filter((u) => {
1133
+ if (!u.memberOf?.some((dn) => privilegedGroupNames.some((pg) => dn.includes(`CN=${pg}`)))) {
1134
+ return false;
1135
+ }
1136
+ const created = u['whenCreated'] as Date | undefined;
1137
+ return created && (now - created.getTime()) < SEVEN_DAYS_MS;
1138
+ });
1139
+
1140
+ if (recentAdmins.length > 0) {
1141
+ issues.push(`${recentAdmins.length} privileged accounts created in last 7 days - verify change requests`);
1142
+ }
1143
+
1144
+ return {
1145
+ type: 'CHANGE_MANAGEMENT_BYPASS',
1146
+ severity: 'high',
1147
+ category: 'compliance',
1148
+ title: 'Potential Change Management Bypass',
1149
+ description:
1150
+ 'Recent privileged changes detected - verify change management process was followed. Required by SOX Section 404, ISO27001 A.12.1.2, SOC2 CC8.1.',
1151
+ count: issues.length > 0 ? 1 : 0,
1152
+ details: issues.length > 0 ? {
1153
+ violations: issues,
1154
+ frameworks: ['SOX', 'ISO27001', 'SOC2'],
1155
+ controls: ['Section 404', 'A.12.1.2', 'CC8.1'],
1156
+ recommendation: 'Implement privileged change approval workflow with audit trail',
1157
+ } : undefined,
1158
+ };
1159
+ }
1160
+
1161
+ /**
1162
+ * VENDOR_ACCOUNT_UNMONITORED - Third-party accounts not monitored
1163
+ * Frameworks: DORA Art.28, ISO27001 A.15.1.1, SOC2 CC9.2
1164
+ * Detects vendor/external accounts that may lack monitoring
1165
+ */
1166
+ export function detectVendorAccountUnmonitored(
1167
+ users: ADUser[],
1168
+ includeDetails: boolean
1169
+ ): Finding {
1170
+ // Patterns that indicate vendor/external accounts
1171
+ const vendorPatterns = [
1172
+ /vendor/i, /external/i, /contractor/i, /consultant/i,
1173
+ /partner/i, /third.?party/i, /supplier/i, /^ext[_-]/i,
1174
+ /^v[_-]/i, /^tmp[_-]/i, /^temp[_-]/i
1175
+ ];
1176
+
1177
+ const vendorAccounts = users.filter((u) => {
1178
+ const name = u.sAMAccountName || '';
1179
+ const desc = (u['description'] as string) || '';
1180
+ const displayName = u.displayName || '';
1181
+
1182
+ return vendorPatterns.some(p =>
1183
+ p.test(name) || p.test(desc) || p.test(displayName)
1184
+ );
1185
+ });
1186
+
1187
+ // Check which vendor accounts lack monitoring indicators
1188
+ const unmonitoredVendors = vendorAccounts.filter((u) => {
1189
+ // Check for account expiration (vendors should have expiry)
1190
+ const accountExpires = u['accountExpires'] as Date | bigint | number | undefined;
1191
+ let hasExpiry = false;
1192
+ if (accountExpires) {
1193
+ if (typeof accountExpires === 'bigint') {
1194
+ // Never expires: 9223372036854775807 or 0
1195
+ hasExpiry = accountExpires !== BigInt('9223372036854775807') && accountExpires !== BigInt(0);
1196
+ } else if (typeof accountExpires === 'number') {
1197
+ hasExpiry = accountExpires !== 9223372036854775807 && accountExpires !== 0;
1198
+ } else if (accountExpires instanceof Date) {
1199
+ hasExpiry = accountExpires.getTime() > Date.now();
1200
+ }
1201
+ }
1202
+
1203
+ // Check for recent activity
1204
+ const lastLogon = u.lastLogon;
1205
+ const NINETY_DAYS_MS = 90 * 24 * 60 * 60 * 1000;
1206
+ const isActive = lastLogon && (Date.now() - lastLogon.getTime()) < NINETY_DAYS_MS;
1207
+
1208
+ // Flag if no expiry AND active (should be monitored)
1209
+ return !hasExpiry && isActive;
1210
+ });
1211
+
1212
+ return {
1213
+ type: 'VENDOR_ACCOUNT_UNMONITORED',
1214
+ severity: 'medium',
1215
+ category: 'compliance',
1216
+ title: 'Vendor Accounts Not Properly Monitored',
1217
+ description:
1218
+ 'Third-party/vendor accounts detected without proper expiration or monitoring controls. Required by DORA Article 28, ISO27001 A.15.1.1, SOC2 CC9.2.',
1219
+ count: unmonitoredVendors.length,
1220
+ affectedEntities: includeDetails ? toAffectedUserEntities(unmonitoredVendors.slice(0, 50)) : undefined,
1221
+ details: unmonitoredVendors.length > 0 ? {
1222
+ totalVendorAccounts: vendorAccounts.length,
1223
+ unmonitoredCount: unmonitoredVendors.length,
1224
+ frameworks: ['DORA', 'ISO27001', 'SOC2'],
1225
+ controls: ['Art.28', 'A.15.1.1', 'CC9.2'],
1226
+ recommendation: 'Set expiration dates and enable enhanced logging for all vendor accounts',
1227
+ } : undefined,
1228
+ };
1229
+ }
1230
+
1231
+ /**
1232
+ * ENCRYPTION_AT_REST_DISABLED - BitLocker not deployed on DCs
1233
+ * Frameworks: PCI-DSS 3.4, HIPAA 164.312(a)(2)(iv), ISO27001 A.10.1.1
1234
+ * Checks if domain controllers have encryption indicators
1235
+ */
1236
+ export function detectEncryptionAtRestDisabled(
1237
+ computers: ADComputer[],
1238
+ _includeDetails: boolean
1239
+ ): Finding {
1240
+ // Find domain controllers
1241
+ const domainControllers = computers.filter((c) => {
1242
+ const uac = c.userAccountControl || 0;
1243
+ return (uac & 0x2000) !== 0; // SERVER_TRUST_ACCOUNT
1244
+ });
1245
+
1246
+ // Check for BitLocker recovery information
1247
+ // In AD, BitLocker keys are stored as msFVE-RecoveryInformation objects linked to computer
1248
+ const dcsWithoutBitLocker = domainControllers.filter((dc) => {
1249
+ // Check for msFVE attributes (BitLocker)
1250
+ const hasBitLocker = dc['msFVE-RecoveryPassword'] || dc['msFVE-KeyPackage'];
1251
+ return !hasBitLocker;
1252
+ });
1253
+
1254
+ // Also check all servers for encryption
1255
+ const servers = computers.filter((c) => {
1256
+ const os = ldapAttrToString(c.operatingSystem).toLowerCase();
1257
+ return os.includes('server') && !domainControllers.includes(c);
1258
+ });
1259
+
1260
+ const serversWithoutBitLocker = servers.filter((s) => {
1261
+ const hasBitLocker = s['msFVE-RecoveryPassword'] || s['msFVE-KeyPackage'];
1262
+ return !hasBitLocker;
1263
+ });
1264
+
1265
+ const issues: string[] = [];
1266
+ if (dcsWithoutBitLocker.length > 0) {
1267
+ issues.push(`${dcsWithoutBitLocker.length}/${domainControllers.length} Domain Controllers without BitLocker`);
1268
+ }
1269
+ if (serversWithoutBitLocker.length > 0 && serversWithoutBitLocker.length === servers.length) {
1270
+ issues.push(`No servers have BitLocker recovery stored in AD`);
1271
+ }
1272
+
1273
+ return {
1274
+ type: 'ENCRYPTION_AT_REST_DISABLED',
1275
+ severity: 'high',
1276
+ category: 'compliance',
1277
+ title: 'Encryption at Rest Not Deployed',
1278
+ description:
1279
+ 'BitLocker encryption not detected on domain controllers or servers. Required by PCI-DSS 3.4, HIPAA 164.312(a)(2)(iv), ISO27001 A.10.1.1.',
1280
+ count: dcsWithoutBitLocker.length,
1281
+ details: issues.length > 0 ? {
1282
+ violations: issues,
1283
+ domainControllers: domainControllers.length,
1284
+ dcsWithBitLocker: domainControllers.length - dcsWithoutBitLocker.length,
1285
+ frameworks: ['PCI-DSS', 'HIPAA', 'ISO27001'],
1286
+ controls: ['3.4', '164.312(a)(2)(iv)', 'A.10.1.1'],
1287
+ recommendation: 'Deploy BitLocker on all domain controllers and servers with AD key backup',
1288
+ } : undefined,
1289
+ };
1290
+ }
1291
+
1292
+ // ==================== COMPLIANCE SCORE ====================
1293
+
1294
+ /**
1295
+ * Framework tracking interface
1296
+ */
1297
+ interface FrameworkScore {
1298
+ total: number;
1299
+ passed: number;
1300
+ }
1301
+
1302
+ /**
1303
+ * Calculate overall compliance score
1304
+ * Provides a summary of compliance across all frameworks
1305
+ */
1306
+ export function detectComplianceScore(
1307
+ findings: Finding[],
1308
+ _includeDetails: boolean
1309
+ ): Finding {
1310
+ // Count compliance findings by framework
1311
+ const anssi: FrameworkScore = { total: 5, passed: 0 };
1312
+ const nist: FrameworkScore = { total: 4, passed: 0 };
1313
+ const cis: FrameworkScore = { total: 3, passed: 0 };
1314
+ const disa: FrameworkScore = { total: 2, passed: 0 };
1315
+ const industry: FrameworkScore = { total: 8, passed: 0 }; // PCI-DSS, SOC2, GDPR, SOX, DORA, HIPAA, ISO27001
1316
+
1317
+ // Industry framework detection types
1318
+ const industryTypes = [
1319
+ 'MFA_NOT_ENFORCED',
1320
+ 'BACKUP_AD_NOT_VERIFIED',
1321
+ 'AUDIT_LOG_RETENTION_SHORT',
1322
+ 'PRIVILEGED_ACCESS_REVIEW_MISSING',
1323
+ 'DATA_CLASSIFICATION_MISSING',
1324
+ 'CHANGE_MANAGEMENT_BYPASS',
1325
+ 'VENDOR_ACCOUNT_UNMONITORED',
1326
+ 'ENCRYPTION_AT_REST_DISABLED',
1327
+ ];
1328
+
1329
+ // Check each compliance finding
1330
+ const complianceFindings = findings.filter((f) => f.category === 'compliance');
1331
+
1332
+ for (const finding of complianceFindings) {
1333
+ if (finding.count === 0) {
1334
+ if (finding.type.startsWith('ANSSI_')) anssi.passed++;
1335
+ else if (finding.type.startsWith('NIST_')) nist.passed++;
1336
+ else if (finding.type.startsWith('CIS_')) cis.passed++;
1337
+ else if (finding.type.startsWith('DISA_')) disa.passed++;
1338
+ else if (industryTypes.includes(finding.type)) industry.passed++;
1339
+ }
1340
+ }
1341
+
1342
+ // Calculate overall score
1343
+ const totalControls = anssi.total + nist.total + cis.total + disa.total + industry.total;
1344
+ const passedControls = anssi.passed + nist.passed + cis.passed + disa.passed + industry.passed;
1345
+ const compliancePercentage = Math.round((passedControls / totalControls) * 100);
1346
+
1347
+ return {
1348
+ type: 'COMPLIANCE_SCORE',
1349
+ severity: 'low',
1350
+ category: 'compliance',
1351
+ title: 'Compliance Score Summary',
1352
+ description: `Overall compliance score: ${compliancePercentage}%. This represents adherence to ANSSI, NIST, CIS, DISA, and industry frameworks (PCI-DSS, SOC2, GDPR, SOX, DORA, HIPAA, ISO27001).`,
1353
+ count: totalControls - passedControls, // Non-compliant controls
1354
+ details: {
1355
+ score: compliancePercentage,
1356
+ frameworks: {
1357
+ ANSSI: `${anssi.passed}/${anssi.total}`,
1358
+ NIST: `${nist.passed}/${nist.total}`,
1359
+ CIS: `${cis.passed}/${cis.total}`,
1360
+ DISA: `${disa.passed}/${disa.total}`,
1361
+ 'Industry (PCI/SOC2/GDPR/SOX/DORA/HIPAA/ISO)': `${industry.passed}/${industry.total}`,
1362
+ },
1363
+ passedControls,
1364
+ totalControls,
1365
+ recommendation:
1366
+ compliancePercentage < 70
1367
+ ? 'Compliance score is below 70%. Prioritize addressing high-severity compliance gaps.'
1368
+ : undefined,
1369
+ },
1370
+ };
1371
+ }
1372
+
1373
+ /**
1374
+ * Detect all compliance vulnerabilities
1375
+ */
1376
+ export function detectComplianceVulnerabilities(
1377
+ users: ADUser[],
1378
+ groups: ADGroup[],
1379
+ computers: ADComputer[],
1380
+ domain: ADDomain,
1381
+ gpoSettings: GpoSecuritySettings | null,
1382
+ includeDetails: boolean
1383
+ ): Finding[] {
1384
+ // Collect all compliance findings except the score
1385
+ const findings = [
1386
+ // ANSSI
1387
+ detectAnssiR1PasswordPolicy(domain, gpoSettings, includeDetails),
1388
+ detectAnssiR2PrivilegedAccounts(users, groups, includeDetails),
1389
+ detectAnssiR3StrongAuth(users, domain, includeDetails),
1390
+ detectAnssiR4Logging(gpoSettings, includeDetails),
1391
+ detectAnssiR5Segregation(users, computers, includeDetails),
1392
+ // NIST
1393
+ detectNistAc2AccountManagement(users, includeDetails),
1394
+ detectNistAc6LeastPrivilege(users, groups, includeDetails),
1395
+ detectNistIa5Authenticator(domain, users, includeDetails),
1396
+ detectNistAu2AuditEvents(gpoSettings, includeDetails),
1397
+ // CIS
1398
+ detectCisPasswordPolicy(domain, includeDetails),
1399
+ detectCisNetworkSecurity(gpoSettings, includeDetails),
1400
+ detectCisUserRights(users, groups, includeDetails),
1401
+ // DISA
1402
+ detectDisaAccountPolicies(domain, users, includeDetails),
1403
+ detectDisaAuditPolicies(gpoSettings, includeDetails),
1404
+ // Industry Frameworks (PCI-DSS, SOC2, GDPR, SOX, DORA, HIPAA, ISO27001)
1405
+ detectMfaNotEnforced(users, includeDetails),
1406
+ detectBackupNotVerified(domain, includeDetails),
1407
+ detectAuditLogRetentionShort(gpoSettings, includeDetails),
1408
+ detectPrivilegedAccessReviewMissing(users, groups, includeDetails),
1409
+ detectDataClassificationMissing(domain, includeDetails),
1410
+ detectChangeManagementBypass(users, groups, includeDetails),
1411
+ detectVendorAccountUnmonitored(users, includeDetails),
1412
+ detectEncryptionAtRestDisabled(computers, includeDetails),
1413
+ ];
1414
+
1415
+ // Add compliance score (calculated from other findings)
1416
+ findings.push(detectComplianceScore(findings, includeDetails));
1417
+
1418
+ // Return all findings (including those with count=0 for score calculation)
1419
+ // But filter out zero-count findings for the final output
1420
+ return findings.filter((f) => f.count > 0 || f.type === 'COMPLIANCE_SCORE');
1421
+ }