@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,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional Access Detector for Azure AD
|
|
3
|
+
*
|
|
4
|
+
* Detects Conditional Access policy vulnerabilities in Azure AD/Entra ID.
|
|
5
|
+
* Story 1.8: Azure Vulnerability Detection Engine
|
|
6
|
+
*
|
|
7
|
+
* Vulnerabilities detected (6):
|
|
8
|
+
* CRITICAL (2):
|
|
9
|
+
* - AZURE_NO_MFA_CA_POLICY
|
|
10
|
+
* - AZURE_NO_LEGACY_AUTH_BLOCK
|
|
11
|
+
*
|
|
12
|
+
* MEDIUM (3):
|
|
13
|
+
* - AZURE_CA_POLICY_DISABLED
|
|
14
|
+
* - AZURE_CA_POLICY_HAS_EXCLUSIONS
|
|
15
|
+
* - AZURE_NO_DEVICE_COMPLIANCE_CA
|
|
16
|
+
*
|
|
17
|
+
* LOW (1):
|
|
18
|
+
* - AZURE_CA_POLICY_REPORT_ONLY
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { AzurePolicy } from '../../../../types/azure.types';
|
|
22
|
+
import { Finding } from '../../../../types/finding.types';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Check for absence of MFA enforcement via Conditional Access
|
|
26
|
+
*/
|
|
27
|
+
export function detectNoMfaCaPolicy(policies: AzurePolicy[], includeDetails: boolean): Finding {
|
|
28
|
+
const hasMfaPolicy = policies.some((policy) => {
|
|
29
|
+
if (policy.state !== 'enabled') return false;
|
|
30
|
+
|
|
31
|
+
const grantControls = (policy as any).grantControls;
|
|
32
|
+
if (!grantControls) return false;
|
|
33
|
+
|
|
34
|
+
const builtInControls = grantControls.builtInControls || [];
|
|
35
|
+
return builtInControls.includes('mfa');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
type: 'AZURE_NO_MFA_CA_POLICY',
|
|
40
|
+
severity: 'critical',
|
|
41
|
+
category: 'conditionalAccess',
|
|
42
|
+
title: 'No MFA Enforcement via Conditional Access',
|
|
43
|
+
description: 'No Conditional Access policy enforcing MFA. Users can authenticate without multi-factor authentication.',
|
|
44
|
+
count: hasMfaPolicy ? 0 : 1,
|
|
45
|
+
affectedEntities: includeDetails && !hasMfaPolicy ? ['Tenant-wide'] : undefined,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check for absence of legacy authentication blocking
|
|
51
|
+
*/
|
|
52
|
+
export function detectNoLegacyAuthBlock(policies: AzurePolicy[], includeDetails: boolean): Finding {
|
|
53
|
+
const hasLegacyAuthBlock = policies.some((policy) => {
|
|
54
|
+
if (policy.state !== 'enabled') return false;
|
|
55
|
+
|
|
56
|
+
const conditions = (policy as any).conditions;
|
|
57
|
+
const clientAppTypes = conditions?.clientAppTypes || [];
|
|
58
|
+
|
|
59
|
+
// Check if policy blocks legacy authentication clients
|
|
60
|
+
const blocksLegacyAuth =
|
|
61
|
+
clientAppTypes.includes('exchangeActiveSync') ||
|
|
62
|
+
clientAppTypes.includes('other') ||
|
|
63
|
+
(clientAppTypes.includes('all') && (policy as any).grantControls?.builtInControls?.includes('block'));
|
|
64
|
+
|
|
65
|
+
return blocksLegacyAuth;
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
type: 'AZURE_NO_LEGACY_AUTH_BLOCK',
|
|
70
|
+
severity: 'critical',
|
|
71
|
+
category: 'conditionalAccess',
|
|
72
|
+
title: 'No Policy Blocking Legacy Authentication',
|
|
73
|
+
description: 'No Conditional Access policy blocking legacy authentication protocols. Legacy auth bypasses MFA.',
|
|
74
|
+
count: hasLegacyAuthBlock ? 0 : 1,
|
|
75
|
+
affectedEntities: includeDetails && !hasLegacyAuthBlock ? ['Tenant-wide'] : undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check for disabled Conditional Access policies
|
|
81
|
+
*/
|
|
82
|
+
export function detectCaPolicyDisabled(policies: AzurePolicy[], includeDetails: boolean): Finding {
|
|
83
|
+
const affected = policies.filter((policy) => {
|
|
84
|
+
return policy.state === 'disabled';
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
type: 'AZURE_CA_POLICY_DISABLED',
|
|
89
|
+
severity: 'medium',
|
|
90
|
+
category: 'conditionalAccess',
|
|
91
|
+
title: 'Disabled Conditional Access Policy',
|
|
92
|
+
description: 'Conditional Access policy is disabled. Security controls are not being enforced.',
|
|
93
|
+
count: affected.length,
|
|
94
|
+
affectedEntities: includeDetails ? affected.map((p) => p.displayName) : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check for CA policies with user/group exclusions
|
|
100
|
+
*/
|
|
101
|
+
export function detectCaPolicyHasExclusions(policies: AzurePolicy[], includeDetails: boolean): Finding {
|
|
102
|
+
const affected = policies.filter((policy) => {
|
|
103
|
+
if (policy.state !== 'enabled') return false;
|
|
104
|
+
|
|
105
|
+
const conditions = (policy as any).conditions;
|
|
106
|
+
const users = conditions?.users;
|
|
107
|
+
|
|
108
|
+
const hasExclusions =
|
|
109
|
+
(users?.excludeUsers && users.excludeUsers.length > 0) ||
|
|
110
|
+
(users?.excludeGroups && users.excludeGroups.length > 0) ||
|
|
111
|
+
(users?.excludeRoles && users.excludeRoles.length > 0);
|
|
112
|
+
|
|
113
|
+
return hasExclusions;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
type: 'AZURE_CA_POLICY_HAS_EXCLUSIONS',
|
|
118
|
+
severity: 'medium',
|
|
119
|
+
category: 'conditionalAccess',
|
|
120
|
+
title: 'Conditional Access Policy with Exclusions',
|
|
121
|
+
description: 'CA policy has user, group, or role exclusions. Excluded identities bypass security controls.',
|
|
122
|
+
count: affected.length,
|
|
123
|
+
affectedEntities: includeDetails ? affected.map((p) => p.displayName) : undefined,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check for absence of device compliance requirement
|
|
129
|
+
*/
|
|
130
|
+
export function detectNoDeviceComplianceCa(policies: AzurePolicy[], includeDetails: boolean): Finding {
|
|
131
|
+
const hasDeviceCompliancePolicy = policies.some((policy) => {
|
|
132
|
+
if (policy.state !== 'enabled') return false;
|
|
133
|
+
|
|
134
|
+
const grantControls = (policy as any).grantControls;
|
|
135
|
+
if (!grantControls) return false;
|
|
136
|
+
|
|
137
|
+
const builtInControls = grantControls.builtInControls || [];
|
|
138
|
+
return builtInControls.includes('compliantDevice') || builtInControls.includes('domainJoinedDevice');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
type: 'AZURE_NO_DEVICE_COMPLIANCE_CA',
|
|
143
|
+
severity: 'medium',
|
|
144
|
+
category: 'conditionalAccess',
|
|
145
|
+
title: 'No Device Compliance Requirement',
|
|
146
|
+
description: 'No Conditional Access policy requiring compliant devices. Unmanaged devices can access resources.',
|
|
147
|
+
count: hasDeviceCompliancePolicy ? 0 : 1,
|
|
148
|
+
affectedEntities: includeDetails && !hasDeviceCompliancePolicy ? ['Tenant-wide'] : undefined,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check for CA policies in report-only mode
|
|
154
|
+
*/
|
|
155
|
+
export function detectCaPolicyReportOnly(policies: AzurePolicy[], includeDetails: boolean): Finding {
|
|
156
|
+
const affected = policies.filter((policy) => {
|
|
157
|
+
return policy.state === 'enabledForReportingButNotEnforced';
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
type: 'AZURE_CA_POLICY_REPORT_ONLY',
|
|
162
|
+
severity: 'low',
|
|
163
|
+
category: 'conditionalAccess',
|
|
164
|
+
title: 'Conditional Access Policy in Report-Only Mode',
|
|
165
|
+
description: 'CA policy in report-only mode. Security controls are not being enforced, only logged.',
|
|
166
|
+
count: affected.length,
|
|
167
|
+
affectedEntities: includeDetails ? affected.map((p) => p.displayName) : undefined,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Detect all Conditional Access vulnerabilities
|
|
173
|
+
*/
|
|
174
|
+
export function detectConditionalAccessVulnerabilities(
|
|
175
|
+
policies: AzurePolicy[],
|
|
176
|
+
includeDetails: boolean
|
|
177
|
+
): Finding[] {
|
|
178
|
+
return [
|
|
179
|
+
detectNoMfaCaPolicy(policies, includeDetails),
|
|
180
|
+
detectNoLegacyAuthBlock(policies, includeDetails),
|
|
181
|
+
detectCaPolicyDisabled(policies, includeDetails),
|
|
182
|
+
detectCaPolicyHasExclusions(policies, includeDetails),
|
|
183
|
+
detectNoDeviceComplianceCa(policies, includeDetails),
|
|
184
|
+
detectCaPolicyReportOnly(policies, includeDetails),
|
|
185
|
+
].filter((finding) => finding.count > 0);
|
|
186
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privilege Security Vulnerability Detector for Azure AD
|
|
3
|
+
*
|
|
4
|
+
* Detects privileged access vulnerabilities in Azure AD/Entra ID.
|
|
5
|
+
* Story 1.8: Azure Vulnerability Detection Engine
|
|
6
|
+
*
|
|
7
|
+
* Vulnerabilities detected (4):
|
|
8
|
+
* CRITICAL (2):
|
|
9
|
+
* - AZURE_GUEST_PRIVILEGED_ACCESS
|
|
10
|
+
* - AZURE_SERVICE_ACCOUNT_PRIVILEGED
|
|
11
|
+
*
|
|
12
|
+
* HIGH (2):
|
|
13
|
+
* - AZURE_TOO_MANY_GLOBAL_ADMINS
|
|
14
|
+
* - AZURE_GROUP_DYNAMIC_RISKY_RULE
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { AzureUser, AzureGroup } from '../../../../types/azure.types';
|
|
18
|
+
import { Finding } from '../../../../types/finding.types';
|
|
19
|
+
import { toAffectedAzureUserEntities, toAffectedAzureGroupEntities } from '../../../../utils/entity-converter';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check for guest users with privileged roles
|
|
23
|
+
*/
|
|
24
|
+
export function detectGuestPrivilegedAccess(
|
|
25
|
+
users: AzureUser[],
|
|
26
|
+
roles: Map<string, string[]>,
|
|
27
|
+
includeDetails: boolean
|
|
28
|
+
): Finding {
|
|
29
|
+
const privilegedRoleIds = [
|
|
30
|
+
'62e90394-69f5-4237-9190-012177145e10', // Global Administrator
|
|
31
|
+
'e8611ab8-c189-46e8-94e1-60213ab1f814', // Privileged Role Administrator
|
|
32
|
+
'194ae4cb-b126-40b2-bd5b-6091b380977d', // Security Administrator
|
|
33
|
+
'29232cdf-9323-42fd-ade2-1d097af3e4de', // Exchange Administrator
|
|
34
|
+
'f28a1f50-f6e7-4571-818b-6a12f2af6b6c', // SharePoint Administrator
|
|
35
|
+
'729827e3-9c14-49f7-bb1b-9608f156bbb8', // Helpdesk Administrator
|
|
36
|
+
'b0f54661-2d74-4c50-afa3-1ec803f12efe', // Billing Administrator
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const affected = users.filter((u) => {
|
|
40
|
+
const isGuest = (u as any).userType === 'Guest';
|
|
41
|
+
const userRoles = roles.get(u.id) || [];
|
|
42
|
+
const hasPrivilegedRole = userRoles.some((r) => privilegedRoleIds.includes(r));
|
|
43
|
+
|
|
44
|
+
return isGuest && hasPrivilegedRole;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
type: 'AZURE_GUEST_PRIVILEGED_ACCESS',
|
|
49
|
+
severity: 'critical',
|
|
50
|
+
category: 'privilegedAccess',
|
|
51
|
+
title: 'Guest User with Privileged Role',
|
|
52
|
+
description: 'Guest user from external domain assigned privileged role. High risk from external account compromise.',
|
|
53
|
+
count: affected.length,
|
|
54
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check for service accounts with privileged roles
|
|
60
|
+
*/
|
|
61
|
+
export function detectServiceAccountPrivileged(
|
|
62
|
+
users: AzureUser[],
|
|
63
|
+
roles: Map<string, string[]>,
|
|
64
|
+
includeDetails: boolean
|
|
65
|
+
): Finding {
|
|
66
|
+
const privilegedRoleIds = [
|
|
67
|
+
'62e90394-69f5-4237-9190-012177145e10', // Global Administrator
|
|
68
|
+
'e8611ab8-c189-46e8-94e1-60213ab1f814', // Privileged Role Administrator
|
|
69
|
+
'194ae4cb-b126-40b2-bd5b-6091b380977d', // Security Administrator
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const affected = users.filter((u) => {
|
|
73
|
+
// Service accounts typically: no sign-in, name patterns, or application type
|
|
74
|
+
const isServiceAccount =
|
|
75
|
+
!u.lastSignInDateTime ||
|
|
76
|
+
u.userPrincipalName?.toLowerCase().includes('svc') ||
|
|
77
|
+
u.userPrincipalName?.toLowerCase().includes('service') ||
|
|
78
|
+
u.displayName?.toLowerCase().includes('service') ||
|
|
79
|
+
(u as any).userType === 'Application';
|
|
80
|
+
|
|
81
|
+
const userRoles = roles.get(u.id) || [];
|
|
82
|
+
const hasPrivilegedRole = userRoles.some((r) => privilegedRoleIds.includes(r));
|
|
83
|
+
|
|
84
|
+
return isServiceAccount && hasPrivilegedRole;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
type: 'AZURE_SERVICE_ACCOUNT_PRIVILEGED',
|
|
89
|
+
severity: 'critical',
|
|
90
|
+
category: 'privilegedAccess',
|
|
91
|
+
title: 'Service Account with Privileged Role',
|
|
92
|
+
description: 'Service/application account with privileged role. Service accounts should use managed identities.',
|
|
93
|
+
count: affected.length,
|
|
94
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check for excessive number of Global Administrators (>5)
|
|
100
|
+
*/
|
|
101
|
+
export function detectTooManyGlobalAdmins(
|
|
102
|
+
users: AzureUser[],
|
|
103
|
+
roles: Map<string, string[]>,
|
|
104
|
+
includeDetails: boolean
|
|
105
|
+
): Finding {
|
|
106
|
+
const globalAdminRoleId = '62e90394-69f5-4237-9190-012177145e10';
|
|
107
|
+
|
|
108
|
+
const affected = users.filter((u) => {
|
|
109
|
+
const userRoles = roles.get(u.id) || [];
|
|
110
|
+
return userRoles.includes(globalAdminRoleId);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const threshold = 5;
|
|
114
|
+
const count = affected.length > threshold ? 1 : 0;
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
type: 'AZURE_TOO_MANY_GLOBAL_ADMINS',
|
|
118
|
+
severity: 'high',
|
|
119
|
+
category: 'privilegedAccess',
|
|
120
|
+
title: 'Too Many Global Administrators',
|
|
121
|
+
description: `Tenant has ${affected.length} Global Administrators (recommended: ≤${threshold}). Excessive admin accounts increase attack surface.`,
|
|
122
|
+
count,
|
|
123
|
+
affectedEntities: includeDetails && count > 0 ? [`${affected.length} Global Admins`] : undefined,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check for dynamic groups with risky membership rules
|
|
129
|
+
*/
|
|
130
|
+
export function detectGroupDynamicRiskyRule(groups: AzureGroup[], includeDetails: boolean): Finding {
|
|
131
|
+
const riskyPatterns = [
|
|
132
|
+
'accountEnabled eq true', // Too broad
|
|
133
|
+
'userType eq "Member"', // All members
|
|
134
|
+
'userType eq "Guest"', // All guests (less risky but broad)
|
|
135
|
+
'objectId ne null', // Everyone
|
|
136
|
+
'mail contains "@"', // Everyone with email
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
const affected = groups.filter((g) => {
|
|
140
|
+
const membershipRule = (g as any).membershipRule;
|
|
141
|
+
if (!membershipRule || (g as any).groupTypes?.includes('DynamicMembership') === false) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check if rule contains risky patterns
|
|
146
|
+
const lowerRule = membershipRule.toLowerCase();
|
|
147
|
+
return riskyPatterns.some((pattern) => lowerRule.includes(pattern.toLowerCase()));
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
type: 'AZURE_GROUP_DYNAMIC_RISKY_RULE',
|
|
152
|
+
severity: 'high',
|
|
153
|
+
category: 'privilegedAccess',
|
|
154
|
+
title: 'Dynamic Group with Risky Membership Rule',
|
|
155
|
+
description: 'Dynamic group with overly permissive membership rule. May grant unintended access.',
|
|
156
|
+
count: affected.length,
|
|
157
|
+
affectedEntities: includeDetails ? toAffectedAzureGroupEntities(affected) : undefined,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Detect all privilege security vulnerabilities
|
|
163
|
+
*/
|
|
164
|
+
export function detectPrivilegeSecurityVulnerabilities(
|
|
165
|
+
users: AzureUser[],
|
|
166
|
+
groups: AzureGroup[],
|
|
167
|
+
roles: Map<string, string[]>,
|
|
168
|
+
includeDetails: boolean
|
|
169
|
+
): Finding[] {
|
|
170
|
+
return [
|
|
171
|
+
detectGuestPrivilegedAccess(users, roles, includeDetails),
|
|
172
|
+
detectServiceAccountPrivileged(users, roles, includeDetails),
|
|
173
|
+
detectTooManyGlobalAdmins(users, roles, includeDetails),
|
|
174
|
+
detectGroupDynamicRiskyRule(groups, includeDetails),
|
|
175
|
+
].filter((finding) => finding.count > 0);
|
|
176
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User Security Vulnerability Detector for Azure AD
|
|
3
|
+
*
|
|
4
|
+
* Detects user-related vulnerabilities in Azure AD/Entra ID.
|
|
5
|
+
* Story 1.8: Azure Vulnerability Detection Engine
|
|
6
|
+
*
|
|
7
|
+
* Vulnerabilities detected (10):
|
|
8
|
+
* CRITICAL (3):
|
|
9
|
+
* - AZURE_GLOBAL_ADMIN_NO_MFA
|
|
10
|
+
* - AZURE_PRIVILEGED_USER_NO_MFA
|
|
11
|
+
* - AZURE_RISKY_USER_HIGH
|
|
12
|
+
*
|
|
13
|
+
* HIGH (3):
|
|
14
|
+
* - AZURE_USER_INACTIVE
|
|
15
|
+
* - AZURE_USER_PASSWORD_NEVER_EXPIRES
|
|
16
|
+
* - AZURE_RISKY_USER_MEDIUM
|
|
17
|
+
*
|
|
18
|
+
* MEDIUM (4):
|
|
19
|
+
* - AZURE_PASSWORD_OLD
|
|
20
|
+
* - AZURE_USER_NEVER_SIGNED_IN
|
|
21
|
+
* - AZURE_USER_UNLICENSED
|
|
22
|
+
* - AZURE_USER_EXTERNAL_MEMBER
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { AzureUser } from '../../../../types/azure.types';
|
|
26
|
+
import { Finding } from '../../../../types/finding.types';
|
|
27
|
+
import { toAffectedAzureUserEntities } from '../../../../utils/entity-converter';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check for Global Administrators without MFA
|
|
31
|
+
*/
|
|
32
|
+
export function detectGlobalAdminNoMfa(
|
|
33
|
+
users: AzureUser[],
|
|
34
|
+
roles: Map<string, string[]>,
|
|
35
|
+
includeDetails: boolean
|
|
36
|
+
): Finding {
|
|
37
|
+
const globalAdminRoleId = '62e90394-69f5-4237-9190-012177145e10'; // Well-known Global Admin role ID
|
|
38
|
+
|
|
39
|
+
const affected = users.filter((u) => {
|
|
40
|
+
const userRoles = roles.get(u.id) || [];
|
|
41
|
+
const isGlobalAdmin = userRoles.includes(globalAdminRoleId);
|
|
42
|
+
const hasMfa = (u as any).strongAuthenticationMethods?.length > 0 || (u as any).isMfaRegistered === true;
|
|
43
|
+
|
|
44
|
+
return isGlobalAdmin && !hasMfa;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
type: 'AZURE_GLOBAL_ADMIN_NO_MFA',
|
|
49
|
+
severity: 'critical',
|
|
50
|
+
category: 'identity',
|
|
51
|
+
title: 'Global Administrator without MFA',
|
|
52
|
+
description: 'Global Administrator account without Multi-Factor Authentication. Full control over Azure AD if compromised.',
|
|
53
|
+
count: affected.length,
|
|
54
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check for privileged users (non-GA) without MFA
|
|
60
|
+
*/
|
|
61
|
+
export function detectPrivilegedUserNoMfa(
|
|
62
|
+
users: AzureUser[],
|
|
63
|
+
roles: Map<string, string[]>,
|
|
64
|
+
includeDetails: boolean
|
|
65
|
+
): Finding {
|
|
66
|
+
const privilegedRoleIds = [
|
|
67
|
+
'62e90394-69f5-4237-9190-012177145e10', // Global Administrator
|
|
68
|
+
'e8611ab8-c189-46e8-94e1-60213ab1f814', // Privileged Role Administrator
|
|
69
|
+
'194ae4cb-b126-40b2-bd5b-6091b380977d', // Security Administrator
|
|
70
|
+
'29232cdf-9323-42fd-ade2-1d097af3e4de', // Exchange Administrator
|
|
71
|
+
'f28a1f50-f6e7-4571-818b-6a12f2af6b6c', // SharePoint Administrator
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const affected = users.filter((u) => {
|
|
75
|
+
const userRoles = roles.get(u.id) || [];
|
|
76
|
+
const hasPrivilegedRole = userRoles.some((r) => privilegedRoleIds.includes(r));
|
|
77
|
+
const hasMfa = (u as any).strongAuthenticationMethods?.length > 0 || (u as any).isMfaRegistered === true;
|
|
78
|
+
|
|
79
|
+
return hasPrivilegedRole && !hasMfa;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
type: 'AZURE_PRIVILEGED_USER_NO_MFA',
|
|
84
|
+
severity: 'critical',
|
|
85
|
+
category: 'identity',
|
|
86
|
+
title: 'Privileged User without MFA',
|
|
87
|
+
description: 'Privileged role assigned to user without MFA. High risk if account is compromised.',
|
|
88
|
+
count: affected.length,
|
|
89
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Check for high-risk users (Identity Protection)
|
|
95
|
+
*/
|
|
96
|
+
export function detectRiskyUserHigh(users: AzureUser[], includeDetails: boolean): Finding {
|
|
97
|
+
const affected = users.filter((u) => {
|
|
98
|
+
const riskLevel = (u as any).riskLevel;
|
|
99
|
+
return riskLevel === 'high';
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
type: 'AZURE_RISKY_USER_HIGH',
|
|
104
|
+
severity: 'critical',
|
|
105
|
+
category: 'identity',
|
|
106
|
+
title: 'High-Risk User (Identity Protection)',
|
|
107
|
+
description: 'User flagged as high risk by Azure Identity Protection. Account may be compromised.',
|
|
108
|
+
count: affected.length,
|
|
109
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check for inactive users (90+ days)
|
|
115
|
+
*/
|
|
116
|
+
export function detectUserInactive(users: AzureUser[], includeDetails: boolean): Finding {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
const ninetyDaysAgo = now - 90 * 24 * 60 * 60 * 1000;
|
|
119
|
+
|
|
120
|
+
const affected = users.filter((u) => {
|
|
121
|
+
if (!u.lastSignInDateTime) return false;
|
|
122
|
+
const lastSignIn = new Date(u.lastSignInDateTime).getTime();
|
|
123
|
+
return lastSignIn < ninetyDaysAgo;
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
type: 'AZURE_USER_INACTIVE',
|
|
128
|
+
severity: 'high',
|
|
129
|
+
category: 'accounts',
|
|
130
|
+
title: 'Inactive User (90+ days)',
|
|
131
|
+
description: 'User account inactive for 90+ days. Should be disabled or deleted.',
|
|
132
|
+
count: affected.length,
|
|
133
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Check for users with password never expires
|
|
139
|
+
*/
|
|
140
|
+
export function detectPasswordNeverExpires(users: AzureUser[], includeDetails: boolean): Finding {
|
|
141
|
+
const affected = users.filter((u) => {
|
|
142
|
+
return (u as any).passwordPolicies?.includes('DisablePasswordExpiration');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
type: 'AZURE_USER_PASSWORD_NEVER_EXPIRES',
|
|
147
|
+
severity: 'high',
|
|
148
|
+
category: 'accounts',
|
|
149
|
+
title: 'Password Never Expires',
|
|
150
|
+
description: 'User password expiration disabled. Increases risk of credential compromise.',
|
|
151
|
+
count: affected.length,
|
|
152
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check for medium-risk users (Identity Protection)
|
|
158
|
+
*/
|
|
159
|
+
export function detectRiskyUserMedium(users: AzureUser[], includeDetails: boolean): Finding {
|
|
160
|
+
const affected = users.filter((u) => {
|
|
161
|
+
const riskLevel = (u as any).riskLevel;
|
|
162
|
+
return riskLevel === 'medium';
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
type: 'AZURE_RISKY_USER_MEDIUM',
|
|
167
|
+
severity: 'high',
|
|
168
|
+
category: 'identity',
|
|
169
|
+
title: 'Medium-Risk User (Identity Protection)',
|
|
170
|
+
description: 'User flagged as medium risk by Azure Identity Protection. Unusual sign-in activity detected.',
|
|
171
|
+
count: affected.length,
|
|
172
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Check for old passwords (180+ days)
|
|
178
|
+
*/
|
|
179
|
+
export function detectPasswordOld(users: AzureUser[], includeDetails: boolean): Finding {
|
|
180
|
+
const now = Date.now();
|
|
181
|
+
const sixMonthsAgo = now - 180 * 24 * 60 * 60 * 1000;
|
|
182
|
+
|
|
183
|
+
const affected = users.filter((u) => {
|
|
184
|
+
const lastPasswordChange = (u as any).lastPasswordChangeDateTime;
|
|
185
|
+
if (!lastPasswordChange) return false;
|
|
186
|
+
return new Date(lastPasswordChange).getTime() < sixMonthsAgo;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
type: 'AZURE_PASSWORD_OLD',
|
|
191
|
+
severity: 'medium',
|
|
192
|
+
category: 'accounts',
|
|
193
|
+
title: 'Old Password (180+ days)',
|
|
194
|
+
description: 'User password not changed for 180+ days. Increases credential compromise risk.',
|
|
195
|
+
count: affected.length,
|
|
196
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Check for users who never signed in
|
|
202
|
+
*/
|
|
203
|
+
export function detectUserNeverSignedIn(users: AzureUser[], includeDetails: boolean): Finding {
|
|
204
|
+
const affected = users.filter((u) => {
|
|
205
|
+
return u.accountEnabled && !u.lastSignInDateTime;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
type: 'AZURE_USER_NEVER_SIGNED_IN',
|
|
210
|
+
severity: 'medium',
|
|
211
|
+
category: 'accounts',
|
|
212
|
+
title: 'User Never Signed In',
|
|
213
|
+
description: 'Enabled account that has never been used. Orphaned account should be removed.',
|
|
214
|
+
count: affected.length,
|
|
215
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Check for unlicensed users
|
|
221
|
+
*/
|
|
222
|
+
export function detectUserUnlicensed(users: AzureUser[], includeDetails: boolean): Finding {
|
|
223
|
+
const affected = users.filter((u) => {
|
|
224
|
+
const licenses = (u as any).assignedLicenses;
|
|
225
|
+
return u.accountEnabled && (!licenses || licenses.length === 0);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
type: 'AZURE_USER_UNLICENSED',
|
|
230
|
+
severity: 'medium',
|
|
231
|
+
category: 'accounts',
|
|
232
|
+
title: 'Unlicensed Active User',
|
|
233
|
+
description: 'Active user without assigned license. May indicate configuration issue.',
|
|
234
|
+
count: affected.length,
|
|
235
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Check for external users with Member type
|
|
241
|
+
*/
|
|
242
|
+
export function detectUserExternalMember(users: AzureUser[], includeDetails: boolean): Finding {
|
|
243
|
+
const affected = users.filter((u) => {
|
|
244
|
+
const userType = (u as any).userType;
|
|
245
|
+
const isExternal = u.userPrincipalName?.includes('#EXT#');
|
|
246
|
+
return userType === 'Member' && isExternal;
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
type: 'AZURE_USER_EXTERNAL_MEMBER',
|
|
251
|
+
severity: 'medium',
|
|
252
|
+
category: 'accounts',
|
|
253
|
+
title: 'External User as Member',
|
|
254
|
+
description: 'External domain user configured as Member instead of Guest. Security misconfiguration.',
|
|
255
|
+
count: affected.length,
|
|
256
|
+
affectedEntities: includeDetails ? toAffectedAzureUserEntities(affected) : undefined,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Detect all user security vulnerabilities
|
|
262
|
+
*/
|
|
263
|
+
export function detectUserSecurityVulnerabilities(
|
|
264
|
+
users: AzureUser[],
|
|
265
|
+
roles: Map<string, string[]>,
|
|
266
|
+
includeDetails: boolean
|
|
267
|
+
): Finding[] {
|
|
268
|
+
return [
|
|
269
|
+
detectGlobalAdminNoMfa(users, roles, includeDetails),
|
|
270
|
+
detectPrivilegedUserNoMfa(users, roles, includeDetails),
|
|
271
|
+
detectRiskyUserHigh(users, includeDetails),
|
|
272
|
+
detectUserInactive(users, includeDetails),
|
|
273
|
+
detectPasswordNeverExpires(users, includeDetails),
|
|
274
|
+
detectRiskyUserMedium(users, includeDetails),
|
|
275
|
+
detectPasswordOld(users, includeDetails),
|
|
276
|
+
detectUserNeverSignedIn(users, includeDetails),
|
|
277
|
+
detectUserUnlicensed(users, includeDetails),
|
|
278
|
+
detectUserExternalMember(users, includeDetails),
|
|
279
|
+
].filter((finding) => finding.count > 0);
|
|
280
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vulnerability Detectors Index
|
|
3
|
+
*
|
|
4
|
+
* Central export for all vulnerability detectors (AD, Azure, Intune, Exchange, etc.)
|
|
5
|
+
* Story 1.7: AD Vulnerability Detection Engine
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Active Directory detectors
|
|
9
|
+
export * as ADDetectors from './ad';
|
|
10
|
+
|
|
11
|
+
// Azure AD/Entra ID detectors (future)
|
|
12
|
+
// export * as AzureDetectors from './azure';
|
|
13
|
+
|
|
14
|
+
// Intune detectors (future)
|
|
15
|
+
// export * as IntuneDetectors from './intune';
|
|
16
|
+
|
|
17
|
+
// Exchange detectors (future)
|
|
18
|
+
// export * as ExchangeDetectors from './exchange';
|