@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.
- package/.env.example +60 -0
- package/.env.test.example +33 -0
- package/.github/workflows/ci.yml +83 -0
- package/.github/workflows/release.yml +246 -0
- package/.prettierrc.json +10 -0
- package/CHANGELOG.md +15 -0
- package/Dockerfile +57 -0
- package/LICENSE +190 -0
- package/README.md +194 -0
- package/dist/api/controllers/audit.controller.d.ts +21 -0
- package/dist/api/controllers/audit.controller.d.ts.map +1 -0
- package/dist/api/controllers/audit.controller.js +179 -0
- package/dist/api/controllers/audit.controller.js.map +1 -0
- package/dist/api/controllers/auth.controller.d.ts +16 -0
- package/dist/api/controllers/auth.controller.d.ts.map +1 -0
- package/dist/api/controllers/auth.controller.js +146 -0
- package/dist/api/controllers/auth.controller.js.map +1 -0
- package/dist/api/controllers/export.controller.d.ts +27 -0
- package/dist/api/controllers/export.controller.d.ts.map +1 -0
- package/dist/api/controllers/export.controller.js +80 -0
- package/dist/api/controllers/export.controller.js.map +1 -0
- package/dist/api/controllers/health.controller.d.ts +5 -0
- package/dist/api/controllers/health.controller.d.ts.map +1 -0
- package/dist/api/controllers/health.controller.js +16 -0
- package/dist/api/controllers/health.controller.js.map +1 -0
- package/dist/api/controllers/jobs.controller.d.ts +13 -0
- package/dist/api/controllers/jobs.controller.d.ts.map +1 -0
- package/dist/api/controllers/jobs.controller.js +125 -0
- package/dist/api/controllers/jobs.controller.js.map +1 -0
- package/dist/api/controllers/providers.controller.d.ts +15 -0
- package/dist/api/controllers/providers.controller.d.ts.map +1 -0
- package/dist/api/controllers/providers.controller.js +112 -0
- package/dist/api/controllers/providers.controller.js.map +1 -0
- package/dist/api/dto/AuditRequest.dto.d.ts +6 -0
- package/dist/api/dto/AuditRequest.dto.d.ts.map +1 -0
- package/dist/api/dto/AuditRequest.dto.js +3 -0
- package/dist/api/dto/AuditRequest.dto.js.map +1 -0
- package/dist/api/dto/AuditResponse.dto.d.ts +17 -0
- package/dist/api/dto/AuditResponse.dto.d.ts.map +1 -0
- package/dist/api/dto/AuditResponse.dto.js +3 -0
- package/dist/api/dto/AuditResponse.dto.js.map +1 -0
- package/dist/api/dto/TokenRequest.dto.d.ts +6 -0
- package/dist/api/dto/TokenRequest.dto.d.ts.map +1 -0
- package/dist/api/dto/TokenRequest.dto.js +3 -0
- package/dist/api/dto/TokenRequest.dto.js.map +1 -0
- package/dist/api/dto/TokenResponse.dto.d.ts +12 -0
- package/dist/api/dto/TokenResponse.dto.d.ts.map +1 -0
- package/dist/api/dto/TokenResponse.dto.js +3 -0
- package/dist/api/dto/TokenResponse.dto.js.map +1 -0
- package/dist/api/middlewares/authenticate.d.ts +12 -0
- package/dist/api/middlewares/authenticate.d.ts.map +1 -0
- package/dist/api/middlewares/authenticate.js +141 -0
- package/dist/api/middlewares/authenticate.js.map +1 -0
- package/dist/api/middlewares/errorHandler.d.ts +3 -0
- package/dist/api/middlewares/errorHandler.d.ts.map +1 -0
- package/dist/api/middlewares/errorHandler.js +30 -0
- package/dist/api/middlewares/errorHandler.js.map +1 -0
- package/dist/api/middlewares/rateLimit.d.ts +3 -0
- package/dist/api/middlewares/rateLimit.d.ts.map +1 -0
- package/dist/api/middlewares/rateLimit.js +34 -0
- package/dist/api/middlewares/rateLimit.js.map +1 -0
- package/dist/api/middlewares/validate.d.ts +4 -0
- package/dist/api/middlewares/validate.d.ts.map +1 -0
- package/dist/api/middlewares/validate.js +31 -0
- package/dist/api/middlewares/validate.js.map +1 -0
- package/dist/api/routes/audit.routes.d.ts +5 -0
- package/dist/api/routes/audit.routes.d.ts.map +1 -0
- package/dist/api/routes/audit.routes.js +24 -0
- package/dist/api/routes/audit.routes.js.map +1 -0
- package/dist/api/routes/auth.routes.d.ts +6 -0
- package/dist/api/routes/auth.routes.d.ts.map +1 -0
- package/dist/api/routes/auth.routes.js +22 -0
- package/dist/api/routes/auth.routes.js.map +1 -0
- package/dist/api/routes/export.routes.d.ts +5 -0
- package/dist/api/routes/export.routes.d.ts.map +1 -0
- package/dist/api/routes/export.routes.js +16 -0
- package/dist/api/routes/export.routes.js.map +1 -0
- package/dist/api/routes/health.routes.d.ts +4 -0
- package/dist/api/routes/health.routes.d.ts.map +1 -0
- package/dist/api/routes/health.routes.js +11 -0
- package/dist/api/routes/health.routes.js.map +1 -0
- package/dist/api/routes/index.d.ts +10 -0
- package/dist/api/routes/index.d.ts.map +1 -0
- package/dist/api/routes/index.js +20 -0
- package/dist/api/routes/index.js.map +1 -0
- package/dist/api/routes/providers.routes.d.ts +5 -0
- package/dist/api/routes/providers.routes.d.ts.map +1 -0
- package/dist/api/routes/providers.routes.js +13 -0
- package/dist/api/routes/providers.routes.js.map +1 -0
- package/dist/api/validators/audit.schemas.d.ts +60 -0
- package/dist/api/validators/audit.schemas.d.ts.map +1 -0
- package/dist/api/validators/audit.schemas.js +55 -0
- package/dist/api/validators/audit.schemas.js.map +1 -0
- package/dist/api/validators/auth.schemas.d.ts +17 -0
- package/dist/api/validators/auth.schemas.d.ts.map +1 -0
- package/dist/api/validators/auth.schemas.js +21 -0
- package/dist/api/validators/auth.schemas.js.map +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +62 -0
- package/dist/app.js.map +1 -0
- package/dist/config/config.schema.d.ts +65 -0
- package/dist/config/config.schema.d.ts.map +1 -0
- package/dist/config/config.schema.js +95 -0
- package/dist/config/config.schema.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +75 -0
- package/dist/config/index.js.map +1 -0
- package/dist/container.d.ts +47 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +137 -0
- package/dist/container.js.map +1 -0
- package/dist/data/database.d.ts +13 -0
- package/dist/data/database.d.ts.map +1 -0
- package/dist/data/database.js +68 -0
- package/dist/data/database.js.map +1 -0
- package/dist/data/jobs/token-cleanup.job.d.ts +23 -0
- package/dist/data/jobs/token-cleanup.job.d.ts.map +1 -0
- package/dist/data/jobs/token-cleanup.job.js +96 -0
- package/dist/data/jobs/token-cleanup.job.js.map +1 -0
- package/dist/data/migrations/migration.runner.d.ts +13 -0
- package/dist/data/migrations/migration.runner.d.ts.map +1 -0
- package/dist/data/migrations/migration.runner.js +136 -0
- package/dist/data/migrations/migration.runner.js.map +1 -0
- package/dist/data/models/Token.model.d.ts +30 -0
- package/dist/data/models/Token.model.d.ts.map +1 -0
- package/dist/data/models/Token.model.js +3 -0
- package/dist/data/models/Token.model.js.map +1 -0
- package/dist/data/repositories/token.repository.d.ts +16 -0
- package/dist/data/repositories/token.repository.d.ts.map +1 -0
- package/dist/data/repositories/token.repository.js +97 -0
- package/dist/data/repositories/token.repository.js.map +1 -0
- package/dist/providers/azure/auth.provider.d.ts +5 -0
- package/dist/providers/azure/auth.provider.d.ts.map +1 -0
- package/dist/providers/azure/auth.provider.js +13 -0
- package/dist/providers/azure/auth.provider.js.map +1 -0
- package/dist/providers/azure/azure-errors.d.ts +40 -0
- package/dist/providers/azure/azure-errors.d.ts.map +1 -0
- package/dist/providers/azure/azure-errors.js +121 -0
- package/dist/providers/azure/azure-errors.js.map +1 -0
- package/dist/providers/azure/azure-retry.d.ts +41 -0
- package/dist/providers/azure/azure-retry.d.ts.map +1 -0
- package/dist/providers/azure/azure-retry.js +85 -0
- package/dist/providers/azure/azure-retry.js.map +1 -0
- package/dist/providers/azure/graph-client.d.ts +26 -0
- package/dist/providers/azure/graph-client.d.ts.map +1 -0
- package/dist/providers/azure/graph-client.js +146 -0
- package/dist/providers/azure/graph-client.js.map +1 -0
- package/dist/providers/azure/graph.provider.d.ts +23 -0
- package/dist/providers/azure/graph.provider.d.ts.map +1 -0
- package/dist/providers/azure/graph.provider.js +161 -0
- package/dist/providers/azure/graph.provider.js.map +1 -0
- package/dist/providers/azure/queries/app.queries.d.ts +6 -0
- package/dist/providers/azure/queries/app.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/app.queries.js +9 -0
- package/dist/providers/azure/queries/app.queries.js.map +1 -0
- package/dist/providers/azure/queries/policy.queries.d.ts +6 -0
- package/dist/providers/azure/queries/policy.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/policy.queries.js +9 -0
- package/dist/providers/azure/queries/policy.queries.js.map +1 -0
- package/dist/providers/azure/queries/user.queries.d.ts +7 -0
- package/dist/providers/azure/queries/user.queries.d.ts.map +1 -0
- package/dist/providers/azure/queries/user.queries.js +10 -0
- package/dist/providers/azure/queries/user.queries.js.map +1 -0
- package/dist/providers/interfaces/IGraphProvider.d.ts +31 -0
- package/dist/providers/interfaces/IGraphProvider.d.ts.map +1 -0
- package/dist/providers/interfaces/IGraphProvider.js +3 -0
- package/dist/providers/interfaces/IGraphProvider.js.map +1 -0
- package/dist/providers/interfaces/ILDAPProvider.d.ts +37 -0
- package/dist/providers/interfaces/ILDAPProvider.d.ts.map +1 -0
- package/dist/providers/interfaces/ILDAPProvider.js +3 -0
- package/dist/providers/interfaces/ILDAPProvider.js.map +1 -0
- package/dist/providers/ldap/acl-parser.d.ts +8 -0
- package/dist/providers/ldap/acl-parser.d.ts.map +1 -0
- package/dist/providers/ldap/acl-parser.js +157 -0
- package/dist/providers/ldap/acl-parser.js.map +1 -0
- package/dist/providers/ldap/ad-mappers.d.ts +8 -0
- package/dist/providers/ldap/ad-mappers.d.ts.map +1 -0
- package/dist/providers/ldap/ad-mappers.js +162 -0
- package/dist/providers/ldap/ad-mappers.js.map +1 -0
- package/dist/providers/ldap/ldap-client.d.ts +33 -0
- package/dist/providers/ldap/ldap-client.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-client.js +195 -0
- package/dist/providers/ldap/ldap-client.js.map +1 -0
- package/dist/providers/ldap/ldap-errors.d.ts +48 -0
- package/dist/providers/ldap/ldap-errors.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-errors.js +120 -0
- package/dist/providers/ldap/ldap-errors.js.map +1 -0
- package/dist/providers/ldap/ldap-retry.d.ts +14 -0
- package/dist/providers/ldap/ldap-retry.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-retry.js +102 -0
- package/dist/providers/ldap/ldap-retry.js.map +1 -0
- package/dist/providers/ldap/ldap-sanitizer.d.ts +12 -0
- package/dist/providers/ldap/ldap-sanitizer.d.ts.map +1 -0
- package/dist/providers/ldap/ldap-sanitizer.js +104 -0
- package/dist/providers/ldap/ldap-sanitizer.js.map +1 -0
- package/dist/providers/ldap/ldap.provider.d.ts +21 -0
- package/dist/providers/ldap/ldap.provider.d.ts.map +1 -0
- package/dist/providers/ldap/ldap.provider.js +165 -0
- package/dist/providers/ldap/ldap.provider.js.map +1 -0
- package/dist/providers/ldap/queries/computer.queries.d.ts +6 -0
- package/dist/providers/ldap/queries/computer.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/computer.queries.js +9 -0
- package/dist/providers/ldap/queries/computer.queries.js.map +1 -0
- package/dist/providers/ldap/queries/group.queries.d.ts +6 -0
- package/dist/providers/ldap/queries/group.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/group.queries.js +9 -0
- package/dist/providers/ldap/queries/group.queries.js.map +1 -0
- package/dist/providers/ldap/queries/user.queries.d.ts +7 -0
- package/dist/providers/ldap/queries/user.queries.d.ts.map +1 -0
- package/dist/providers/ldap/queries/user.queries.js +10 -0
- package/dist/providers/ldap/queries/user.queries.js.map +1 -0
- package/dist/providers/smb/smb.provider.d.ts +68 -0
- package/dist/providers/smb/smb.provider.d.ts.map +1 -0
- package/dist/providers/smb/smb.provider.js +382 -0
- package/dist/providers/smb/smb.provider.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +44 -0
- package/dist/server.js.map +1 -0
- package/dist/services/audit/ad-audit.service.d.ts +70 -0
- package/dist/services/audit/ad-audit.service.d.ts.map +1 -0
- package/dist/services/audit/ad-audit.service.js +1019 -0
- package/dist/services/audit/ad-audit.service.js.map +1 -0
- package/dist/services/audit/attack-graph.service.d.ts +62 -0
- package/dist/services/audit/attack-graph.service.d.ts.map +1 -0
- package/dist/services/audit/attack-graph.service.js +702 -0
- package/dist/services/audit/attack-graph.service.js.map +1 -0
- package/dist/services/audit/audit.service.d.ts +4 -0
- package/dist/services/audit/audit.service.d.ts.map +1 -0
- package/dist/services/audit/audit.service.js +10 -0
- package/dist/services/audit/audit.service.js.map +1 -0
- package/dist/services/audit/azure-audit.service.d.ts +37 -0
- package/dist/services/audit/azure-audit.service.d.ts.map +1 -0
- package/dist/services/audit/azure-audit.service.js +153 -0
- package/dist/services/audit/azure-audit.service.js.map +1 -0
- package/dist/services/audit/detectors/ad/accounts.detector.d.ts +37 -0
- package/dist/services/audit/detectors/ad/accounts.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/accounts.detector.js +881 -0
- package/dist/services/audit/detectors/ad/accounts.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/adcs.detector.d.ts +21 -0
- package/dist/services/audit/detectors/ad/adcs.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/adcs.detector.js +227 -0
- package/dist/services/audit/detectors/ad/adcs.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/advanced.detector.d.ts +63 -0
- package/dist/services/audit/detectors/ad/advanced.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/advanced.detector.js +867 -0
- package/dist/services/audit/detectors/ad/advanced.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts +16 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.js +369 -0
- package/dist/services/audit/detectors/ad/attack-paths.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/compliance.detector.d.ts +28 -0
- package/dist/services/audit/detectors/ad/compliance.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/compliance.detector.js +896 -0
- package/dist/services/audit/detectors/ad/compliance.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/computers.detector.d.ts +30 -0
- package/dist/services/audit/detectors/ad/computers.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/computers.detector.js +799 -0
- package/dist/services/audit/detectors/ad/computers.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/gpo.detector.d.ts +17 -0
- package/dist/services/audit/detectors/ad/gpo.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/gpo.detector.js +257 -0
- package/dist/services/audit/detectors/ad/gpo.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/groups.detector.d.ts +19 -0
- package/dist/services/audit/detectors/ad/groups.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/groups.detector.js +488 -0
- package/dist/services/audit/detectors/ad/groups.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/index.d.ts +15 -0
- package/dist/services/audit/detectors/ad/index.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/index.js +51 -0
- package/dist/services/audit/detectors/ad/index.js.map +1 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.d.ts +17 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.js +293 -0
- package/dist/services/audit/detectors/ad/kerberos.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.d.ts +23 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.js +328 -0
- package/dist/services/audit/detectors/ad/monitoring.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/network.detector.d.ts +39 -0
- package/dist/services/audit/detectors/ad/network.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/network.detector.js +257 -0
- package/dist/services/audit/detectors/ad/network.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/password.detector.d.ts +14 -0
- package/dist/services/audit/detectors/ad/password.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/password.detector.js +235 -0
- package/dist/services/audit/detectors/ad/password.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/permissions.detector.d.ts +20 -0
- package/dist/services/audit/detectors/ad/permissions.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/permissions.detector.js +392 -0
- package/dist/services/audit/detectors/ad/permissions.detector.js.map +1 -0
- package/dist/services/audit/detectors/ad/trusts.detector.d.ts +11 -0
- package/dist/services/audit/detectors/ad/trusts.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/ad/trusts.detector.js +186 -0
- package/dist/services/audit/detectors/ad/trusts.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/app-security.detector.d.ts +11 -0
- package/dist/services/audit/detectors/azure/app-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/app-security.detector.js +184 -0
- package/dist/services/audit/detectors/azure/app-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts +10 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.js +130 -0
- package/dist/services/audit/detectors/azure/conditional-access.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts +8 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.js +113 -0
- package/dist/services/audit/detectors/azure/privilege-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/azure/user-security.detector.d.ts +14 -0
- package/dist/services/audit/detectors/azure/user-security.detector.d.ts.map +1 -0
- package/dist/services/audit/detectors/azure/user-security.detector.js +198 -0
- package/dist/services/audit/detectors/azure/user-security.detector.js.map +1 -0
- package/dist/services/audit/detectors/index.d.ts +2 -0
- package/dist/services/audit/detectors/index.d.ts.map +1 -0
- package/dist/services/audit/detectors/index.js +38 -0
- package/dist/services/audit/detectors/index.js.map +1 -0
- package/dist/services/audit/response-formatter.d.ts +176 -0
- package/dist/services/audit/response-formatter.d.ts.map +1 -0
- package/dist/services/audit/response-formatter.js +240 -0
- package/dist/services/audit/response-formatter.js.map +1 -0
- package/dist/services/audit/scoring.service.d.ts +15 -0
- package/dist/services/audit/scoring.service.d.ts.map +1 -0
- package/dist/services/audit/scoring.service.js +139 -0
- package/dist/services/audit/scoring.service.js.map +1 -0
- package/dist/services/auth/crypto.service.d.ts +19 -0
- package/dist/services/auth/crypto.service.d.ts.map +1 -0
- package/dist/services/auth/crypto.service.js +135 -0
- package/dist/services/auth/crypto.service.js.map +1 -0
- package/dist/services/auth/errors.d.ts +19 -0
- package/dist/services/auth/errors.d.ts.map +1 -0
- package/dist/services/auth/errors.js +46 -0
- package/dist/services/auth/errors.js.map +1 -0
- package/dist/services/auth/token.service.d.ts +41 -0
- package/dist/services/auth/token.service.d.ts.map +1 -0
- package/dist/services/auth/token.service.js +208 -0
- package/dist/services/auth/token.service.js.map +1 -0
- package/dist/services/config/config.service.d.ts +6 -0
- package/dist/services/config/config.service.d.ts.map +1 -0
- package/dist/services/config/config.service.js +64 -0
- package/dist/services/config/config.service.js.map +1 -0
- package/dist/services/export/export.service.d.ts +28 -0
- package/dist/services/export/export.service.d.ts.map +1 -0
- package/dist/services/export/export.service.js +28 -0
- package/dist/services/export/export.service.js.map +1 -0
- package/dist/services/export/formatters/csv.formatter.d.ts +8 -0
- package/dist/services/export/formatters/csv.formatter.d.ts.map +1 -0
- package/dist/services/export/formatters/csv.formatter.js +46 -0
- package/dist/services/export/formatters/csv.formatter.js.map +1 -0
- package/dist/services/export/formatters/json.formatter.d.ts +40 -0
- package/dist/services/export/formatters/json.formatter.d.ts.map +1 -0
- package/dist/services/export/formatters/json.formatter.js +58 -0
- package/dist/services/export/formatters/json.formatter.js.map +1 -0
- package/dist/services/jobs/azure-job-runner.d.ts +38 -0
- package/dist/services/jobs/azure-job-runner.d.ts.map +1 -0
- package/dist/services/jobs/azure-job-runner.js +199 -0
- package/dist/services/jobs/azure-job-runner.js.map +1 -0
- package/dist/services/jobs/index.d.ts +4 -0
- package/dist/services/jobs/index.d.ts.map +1 -0
- package/dist/services/jobs/index.js +20 -0
- package/dist/services/jobs/index.js.map +1 -0
- package/dist/services/jobs/job-runner.d.ts +64 -0
- package/dist/services/jobs/job-runner.d.ts.map +1 -0
- package/dist/services/jobs/job-runner.js +952 -0
- package/dist/services/jobs/job-runner.js.map +1 -0
- package/dist/services/jobs/job-store.d.ts +27 -0
- package/dist/services/jobs/job-store.d.ts.map +1 -0
- package/dist/services/jobs/job-store.js +261 -0
- package/dist/services/jobs/job-store.js.map +1 -0
- package/dist/services/jobs/job.types.d.ts +67 -0
- package/dist/services/jobs/job.types.d.ts.map +1 -0
- package/dist/services/jobs/job.types.js +36 -0
- package/dist/services/jobs/job.types.js.map +1 -0
- package/dist/types/ad.types.d.ts +74 -0
- package/dist/types/ad.types.d.ts.map +1 -0
- package/dist/types/ad.types.js +3 -0
- package/dist/types/ad.types.js.map +1 -0
- package/dist/types/adcs.types.d.ts +58 -0
- package/dist/types/adcs.types.d.ts.map +1 -0
- package/dist/types/adcs.types.js +38 -0
- package/dist/types/adcs.types.js.map +1 -0
- package/dist/types/attack-graph.types.d.ts +135 -0
- package/dist/types/attack-graph.types.d.ts.map +1 -0
- package/dist/types/attack-graph.types.js +58 -0
- package/dist/types/attack-graph.types.js.map +1 -0
- package/dist/types/audit.types.d.ts +34 -0
- package/dist/types/audit.types.d.ts.map +1 -0
- package/dist/types/audit.types.js +3 -0
- package/dist/types/audit.types.js.map +1 -0
- package/dist/types/azure.types.d.ts +61 -0
- package/dist/types/azure.types.d.ts.map +1 -0
- package/dist/types/azure.types.js +3 -0
- package/dist/types/azure.types.js.map +1 -0
- package/dist/types/config.types.d.ts +63 -0
- package/dist/types/config.types.d.ts.map +1 -0
- package/dist/types/config.types.js +3 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/error.types.d.ts +33 -0
- package/dist/types/error.types.d.ts.map +1 -0
- package/dist/types/error.types.js +70 -0
- package/dist/types/error.types.js.map +1 -0
- package/dist/types/finding.types.d.ts +133 -0
- package/dist/types/finding.types.d.ts.map +1 -0
- package/dist/types/finding.types.js +3 -0
- package/dist/types/finding.types.js.map +1 -0
- package/dist/types/gpo.types.d.ts +39 -0
- package/dist/types/gpo.types.d.ts.map +1 -0
- package/dist/types/gpo.types.js +15 -0
- package/dist/types/gpo.types.js.map +1 -0
- package/dist/types/token.types.d.ts +26 -0
- package/dist/types/token.types.d.ts.map +1 -0
- package/dist/types/token.types.js +3 -0
- package/dist/types/token.types.js.map +1 -0
- package/dist/types/trust.types.d.ts +45 -0
- package/dist/types/trust.types.d.ts.map +1 -0
- package/dist/types/trust.types.js +71 -0
- package/dist/types/trust.types.js.map +1 -0
- package/dist/utils/entity-converter.d.ts +17 -0
- package/dist/utils/entity-converter.d.ts.map +1 -0
- package/dist/utils/entity-converter.js +285 -0
- package/dist/utils/entity-converter.js.map +1 -0
- package/dist/utils/graph.util.d.ts +66 -0
- package/dist/utils/graph.util.d.ts.map +1 -0
- package/dist/utils/graph.util.js +382 -0
- package/dist/utils/graph.util.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/type-name-normalizer.d.ts +5 -0
- package/dist/utils/type-name-normalizer.d.ts.map +1 -0
- package/dist/utils/type-name-normalizer.js +218 -0
- package/dist/utils/type-name-normalizer.js.map +1 -0
- package/docker-compose.yml +26 -0
- package/docs/api/README.md +178 -0
- package/docs/api/openapi.yaml +1524 -0
- package/eslint.config.js +54 -0
- package/jest.config.js +38 -0
- package/package.json +97 -0
- package/scripts/fetch-ad-cert.sh +142 -0
- package/src/.gitkeep +0 -0
- package/src/api/.gitkeep +0 -0
- package/src/api/controllers/.gitkeep +0 -0
- package/src/api/controllers/audit.controller.ts +313 -0
- package/src/api/controllers/auth.controller.ts +258 -0
- package/src/api/controllers/export.controller.ts +153 -0
- package/src/api/controllers/health.controller.ts +16 -0
- package/src/api/controllers/jobs.controller.ts +187 -0
- package/src/api/controllers/providers.controller.ts +165 -0
- package/src/api/dto/.gitkeep +0 -0
- package/src/api/dto/AuditRequest.dto.ts +8 -0
- package/src/api/dto/AuditResponse.dto.ts +19 -0
- package/src/api/dto/TokenRequest.dto.ts +8 -0
- package/src/api/dto/TokenResponse.dto.ts +14 -0
- package/src/api/middlewares/.gitkeep +0 -0
- package/src/api/middlewares/authenticate.ts +203 -0
- package/src/api/middlewares/errorHandler.ts +54 -0
- package/src/api/middlewares/rateLimit.ts +35 -0
- package/src/api/middlewares/validate.ts +32 -0
- package/src/api/routes/.gitkeep +0 -0
- package/src/api/routes/audit.routes.ts +77 -0
- package/src/api/routes/auth.routes.ts +71 -0
- package/src/api/routes/export.routes.ts +34 -0
- package/src/api/routes/health.routes.ts +14 -0
- package/src/api/routes/index.ts +40 -0
- package/src/api/routes/providers.routes.ts +24 -0
- package/src/api/validators/.gitkeep +0 -0
- package/src/api/validators/audit.schemas.ts +59 -0
- package/src/api/validators/auth.schemas.ts +59 -0
- package/src/app.ts +87 -0
- package/src/config/.gitkeep +0 -0
- package/src/config/config.schema.ts +108 -0
- package/src/config/index.ts +82 -0
- package/src/container.ts +221 -0
- package/src/data/.gitkeep +0 -0
- package/src/data/database.ts +78 -0
- package/src/data/jobs/token-cleanup.job.ts +166 -0
- package/src/data/migrations/.gitkeep +0 -0
- package/src/data/migrations/001_initial_schema.sql +47 -0
- package/src/data/migrations/migration.runner.ts +125 -0
- package/src/data/models/.gitkeep +0 -0
- package/src/data/models/Token.model.ts +35 -0
- package/src/data/repositories/.gitkeep +0 -0
- package/src/data/repositories/token.repository.ts +160 -0
- package/src/providers/.gitkeep +0 -0
- package/src/providers/azure/.gitkeep +0 -0
- package/src/providers/azure/auth.provider.ts +14 -0
- package/src/providers/azure/azure-errors.ts +189 -0
- package/src/providers/azure/azure-retry.ts +168 -0
- package/src/providers/azure/graph-client.ts +315 -0
- package/src/providers/azure/graph.provider.ts +294 -0
- package/src/providers/azure/queries/app.queries.ts +9 -0
- package/src/providers/azure/queries/policy.queries.ts +9 -0
- package/src/providers/azure/queries/user.queries.ts +10 -0
- package/src/providers/interfaces/.gitkeep +0 -0
- package/src/providers/interfaces/IGraphProvider.ts +117 -0
- package/src/providers/interfaces/ILDAPProvider.ts +142 -0
- package/src/providers/ldap/.gitkeep +0 -0
- package/src/providers/ldap/acl-parser.ts +231 -0
- package/src/providers/ldap/ad-mappers.ts +280 -0
- package/src/providers/ldap/ldap-client.ts +259 -0
- package/src/providers/ldap/ldap-errors.ts +188 -0
- package/src/providers/ldap/ldap-retry.ts +267 -0
- package/src/providers/ldap/ldap-sanitizer.ts +273 -0
- package/src/providers/ldap/ldap.provider.ts +293 -0
- package/src/providers/ldap/queries/computer.queries.ts +9 -0
- package/src/providers/ldap/queries/group.queries.ts +9 -0
- package/src/providers/ldap/queries/user.queries.ts +10 -0
- package/src/providers/smb/smb.provider.ts +653 -0
- package/src/server.ts +60 -0
- package/src/services/.gitkeep +0 -0
- package/src/services/audit/.gitkeep +0 -0
- package/src/services/audit/ad-audit.service.ts +1481 -0
- package/src/services/audit/attack-graph.service.ts +1104 -0
- package/src/services/audit/audit.service.ts +12 -0
- package/src/services/audit/azure-audit.service.ts +286 -0
- package/src/services/audit/detectors/ad/accounts.detector.ts +1232 -0
- package/src/services/audit/detectors/ad/adcs.detector.ts +449 -0
- package/src/services/audit/detectors/ad/advanced.detector.ts +1270 -0
- package/src/services/audit/detectors/ad/attack-paths.detector.ts +600 -0
- package/src/services/audit/detectors/ad/compliance.detector.ts +1421 -0
- package/src/services/audit/detectors/ad/computers.detector.ts +1188 -0
- package/src/services/audit/detectors/ad/gpo.detector.ts +485 -0
- package/src/services/audit/detectors/ad/groups.detector.ts +685 -0
- package/src/services/audit/detectors/ad/index.ts +84 -0
- package/src/services/audit/detectors/ad/kerberos.detector.ts +424 -0
- package/src/services/audit/detectors/ad/monitoring.detector.ts +501 -0
- package/src/services/audit/detectors/ad/network.detector.ts +538 -0
- package/src/services/audit/detectors/ad/password.detector.ts +324 -0
- package/src/services/audit/detectors/ad/permissions.detector.ts +637 -0
- package/src/services/audit/detectors/ad/trusts.detector.ts +315 -0
- package/src/services/audit/detectors/azure/app-security.detector.ts +246 -0
- package/src/services/audit/detectors/azure/conditional-access.detector.ts +186 -0
- package/src/services/audit/detectors/azure/privilege-security.detector.ts +176 -0
- package/src/services/audit/detectors/azure/user-security.detector.ts +280 -0
- package/src/services/audit/detectors/index.ts +18 -0
- package/src/services/audit/response-formatter.ts +604 -0
- package/src/services/audit/scoring.service.ts +234 -0
- package/src/services/auth/.gitkeep +0 -0
- package/src/services/auth/crypto.service.ts +230 -0
- package/src/services/auth/errors.ts +47 -0
- package/src/services/auth/token.service.ts +420 -0
- package/src/services/config/.gitkeep +0 -0
- package/src/services/config/config.service.ts +75 -0
- package/src/services/export/.gitkeep +0 -0
- package/src/services/export/export.service.ts +99 -0
- package/src/services/export/formatters/csv.formatter.ts +124 -0
- package/src/services/export/formatters/json.formatter.ts +160 -0
- package/src/services/jobs/azure-job-runner.ts +312 -0
- package/src/services/jobs/index.ts +9 -0
- package/src/services/jobs/job-runner.ts +1280 -0
- package/src/services/jobs/job-store.ts +384 -0
- package/src/services/jobs/job.types.ts +182 -0
- package/src/types/.gitkeep +0 -0
- package/src/types/ad.types.ts +91 -0
- package/src/types/adcs.types.ts +107 -0
- package/src/types/attack-graph.types.ts +260 -0
- package/src/types/audit.types.ts +42 -0
- package/src/types/azure.types.ts +68 -0
- package/src/types/config.types.ts +79 -0
- package/src/types/error.types.ts +69 -0
- package/src/types/finding.types.ts +284 -0
- package/src/types/gpo.types.ts +72 -0
- package/src/types/smb2.d.ts +73 -0
- package/src/types/token.types.ts +32 -0
- package/src/types/trust.types.ts +140 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/entity-converter.ts +453 -0
- package/src/utils/graph.util.ts +609 -0
- package/src/utils/logger.ts +111 -0
- package/src/utils/type-name-normalizer.ts +302 -0
- package/tests/.gitkeep +0 -0
- package/tests/e2e/.gitkeep +0 -0
- package/tests/fixtures/.gitkeep +0 -0
- package/tests/integration/.gitkeep +0 -0
- package/tests/integration/README.md +156 -0
- package/tests/integration/ad-audit.integration.test.ts +216 -0
- package/tests/integration/api/.gitkeep +0 -0
- package/tests/integration/api/endpoints.integration.test.ts +431 -0
- package/tests/integration/auth/jwt-authentication.integration.test.ts +358 -0
- package/tests/integration/providers/.gitkeep +0 -0
- package/tests/integration/providers/azure-basic.integration.test.ts +167 -0
- package/tests/integration/providers/ldap-basic.integration.test.ts +152 -0
- package/tests/integration/providers/ldap-connectivity.test.ts +44 -0
- package/tests/integration/providers/ldap-provider.integration.test.ts +347 -0
- package/tests/mocks/.gitkeep +0 -0
- package/tests/setup.ts +16 -0
- package/tests/unit/.gitkeep +0 -0
- package/tests/unit/api/middlewares/authenticate.test.ts +446 -0
- package/tests/unit/providers/.gitkeep +0 -0
- package/tests/unit/providers/azure/azure-errors.test.ts +193 -0
- package/tests/unit/providers/azure/azure-retry.test.ts +254 -0
- package/tests/unit/providers/azure/graph-provider.test.ts +313 -0
- package/tests/unit/providers/ldap/ad-mappers.test.ts +392 -0
- package/tests/unit/providers/ldap/ldap-provider.test.ts +376 -0
- package/tests/unit/providers/ldap/ldap-retry.test.ts +377 -0
- package/tests/unit/providers/ldap/ldap-sanitizer.test.ts +301 -0
- package/tests/unit/sample.test.ts +19 -0
- package/tests/unit/services/.gitkeep +0 -0
- package/tests/unit/services/audit/detectors/ad/accounts.detector.test.ts +393 -0
- package/tests/unit/services/audit/detectors/ad/advanced.detector.test.ts +380 -0
- package/tests/unit/services/audit/detectors/ad/computers.detector.test.ts +440 -0
- package/tests/unit/services/audit/detectors/ad/groups.detector.test.ts +276 -0
- package/tests/unit/services/audit/detectors/ad/kerberos.detector.test.ts +215 -0
- package/tests/unit/services/audit/detectors/ad/password.detector.test.ts +226 -0
- package/tests/unit/services/audit/detectors/ad/permissions.detector.test.ts +244 -0
- package/tests/unit/services/audit/detectors/azure/app-security.detector.test.ts +349 -0
- package/tests/unit/services/audit/detectors/azure/conditional-access.detector.test.ts +374 -0
- package/tests/unit/services/audit/detectors/azure/privilege-security.detector.test.ts +374 -0
- package/tests/unit/services/audit/detectors/azure/user-security.detector.test.ts +297 -0
- package/tests/unit/services/auth/crypto.service.test.ts +296 -0
- package/tests/unit/services/auth/token.service.test.ts +579 -0
- package/tests/unit/services/export/export.service.test.ts +241 -0
- package/tests/unit/services/export/formatters/csv.formatter.test.ts +270 -0
- package/tests/unit/services/export/formatters/json.formatter.test.ts +258 -0
- package/tests/unit/utils/.gitkeep +0 -0
- 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
|
+
}
|