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