@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,377 @@
|
|
|
1
|
+
import {
|
|
2
|
+
retryWithBackoff,
|
|
3
|
+
retryable,
|
|
4
|
+
isRetryableError,
|
|
5
|
+
LDAP_RETRY_OPTIONS,
|
|
6
|
+
} from '../../../../src/providers/ldap/ldap-retry';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Unit Tests for LDAP Retry Logic
|
|
10
|
+
* Task 8: Write Unit Tests for LDAP Provider (Story 1.5)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Mock logger to avoid console output during tests
|
|
14
|
+
jest.mock('../../../../src/utils/logger', () => ({
|
|
15
|
+
logger: {
|
|
16
|
+
info: jest.fn(),
|
|
17
|
+
warn: jest.fn(),
|
|
18
|
+
error: jest.fn(),
|
|
19
|
+
debug: jest.fn(),
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
describe('LDAP Retry Logic', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('isRetryableError', () => {
|
|
29
|
+
it('should identify ECONNREFUSED as retryable', () => {
|
|
30
|
+
const error = new Error('Connection refused') as Error & { code: string };
|
|
31
|
+
error.code = 'ECONNREFUSED';
|
|
32
|
+
|
|
33
|
+
expect(isRetryableError(error)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should identify ECONNRESET as retryable', () => {
|
|
37
|
+
const error = new Error('Connection reset') as Error & { code: string };
|
|
38
|
+
error.code = 'ECONNRESET';
|
|
39
|
+
|
|
40
|
+
expect(isRetryableError(error)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should identify ETIMEDOUT as retryable', () => {
|
|
44
|
+
const error = new Error('Timeout') as Error & { code: string };
|
|
45
|
+
error.code = 'ETIMEDOUT';
|
|
46
|
+
|
|
47
|
+
expect(isRetryableError(error)).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should identify timeout message as retryable', () => {
|
|
51
|
+
const error = new Error('Operation timed out');
|
|
52
|
+
|
|
53
|
+
expect(isRetryableError(error)).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should identify connection errors as retryable', () => {
|
|
57
|
+
const error = new Error('Connection error occurred');
|
|
58
|
+
|
|
59
|
+
expect(isRetryableError(error)).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should identify network errors as retryable', () => {
|
|
63
|
+
const error = new Error('Network is unavailable');
|
|
64
|
+
|
|
65
|
+
expect(isRetryableError(error)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should NOT identify authentication errors as retryable', () => {
|
|
69
|
+
const error = new Error('Invalid credentials');
|
|
70
|
+
|
|
71
|
+
expect(isRetryableError(error)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should NOT identify bind failures as retryable', () => {
|
|
75
|
+
const error = new Error('Bind failed: invalid DN');
|
|
76
|
+
|
|
77
|
+
expect(isRetryableError(error)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should NOT identify authentication failed as retryable', () => {
|
|
81
|
+
const error = new Error('Authentication failed');
|
|
82
|
+
|
|
83
|
+
expect(isRetryableError(error)).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('retryWithBackoff', () => {
|
|
88
|
+
it('should succeed on first attempt', async () => {
|
|
89
|
+
const operation = jest.fn().mockResolvedValue('success');
|
|
90
|
+
|
|
91
|
+
const result = await retryWithBackoff(operation, { maxAttempts: 3 });
|
|
92
|
+
|
|
93
|
+
expect(result).toBe('success');
|
|
94
|
+
expect(operation).toHaveBeenCalledTimes(1);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should retry on failure and eventually succeed', async () => {
|
|
98
|
+
const operation = jest
|
|
99
|
+
.fn()
|
|
100
|
+
.mockRejectedValueOnce(new Error('Temporary failure'))
|
|
101
|
+
.mockRejectedValueOnce(new Error('Another failure'))
|
|
102
|
+
.mockResolvedValue('success');
|
|
103
|
+
|
|
104
|
+
const result = await retryWithBackoff(operation, {
|
|
105
|
+
maxAttempts: 3,
|
|
106
|
+
initialDelay: 10,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(result).toBe('success');
|
|
110
|
+
expect(operation).toHaveBeenCalledTimes(3);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should throw error after max attempts', async () => {
|
|
114
|
+
const operation = jest.fn().mockRejectedValue(new Error('Persistent failure'));
|
|
115
|
+
|
|
116
|
+
await expect(
|
|
117
|
+
retryWithBackoff(operation, {
|
|
118
|
+
maxAttempts: 3,
|
|
119
|
+
initialDelay: 10,
|
|
120
|
+
})
|
|
121
|
+
).rejects.toThrow('Persistent failure');
|
|
122
|
+
|
|
123
|
+
expect(operation).toHaveBeenCalledTimes(3);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should not retry if shouldRetry returns false', async () => {
|
|
127
|
+
const operation = jest.fn().mockRejectedValue(new Error('Auth failure'));
|
|
128
|
+
|
|
129
|
+
await expect(
|
|
130
|
+
retryWithBackoff(operation, {
|
|
131
|
+
maxAttempts: 3,
|
|
132
|
+
initialDelay: 10,
|
|
133
|
+
shouldRetry: () => false,
|
|
134
|
+
})
|
|
135
|
+
).rejects.toThrow('Auth failure');
|
|
136
|
+
|
|
137
|
+
expect(operation).toHaveBeenCalledTimes(1);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should call onRetry callback before each retry', async () => {
|
|
141
|
+
const operation = jest
|
|
142
|
+
.fn()
|
|
143
|
+
.mockRejectedValueOnce(new Error('Failure 1'))
|
|
144
|
+
.mockResolvedValue('success');
|
|
145
|
+
|
|
146
|
+
const onRetry = jest.fn();
|
|
147
|
+
|
|
148
|
+
await retryWithBackoff(operation, {
|
|
149
|
+
maxAttempts: 3,
|
|
150
|
+
initialDelay: 10,
|
|
151
|
+
onRetry,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(onRetry).toHaveBeenCalledTimes(1);
|
|
155
|
+
expect(onRetry).toHaveBeenCalledWith(
|
|
156
|
+
expect.objectContaining({ message: 'Failure 1' }),
|
|
157
|
+
0,
|
|
158
|
+
expect.any(Number)
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should apply exponential backoff', async () => {
|
|
163
|
+
const operation = jest
|
|
164
|
+
.fn()
|
|
165
|
+
.mockRejectedValueOnce(new Error('Failure 1'))
|
|
166
|
+
.mockRejectedValueOnce(new Error('Failure 2'))
|
|
167
|
+
.mockResolvedValue('success');
|
|
168
|
+
|
|
169
|
+
const delays: number[] = [];
|
|
170
|
+
const onRetry = jest.fn((_error, _attempt, delay) => {
|
|
171
|
+
delays.push(delay);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const startTime = Date.now();
|
|
175
|
+
|
|
176
|
+
await retryWithBackoff(operation, {
|
|
177
|
+
maxAttempts: 3,
|
|
178
|
+
initialDelay: 100,
|
|
179
|
+
backoffMultiplier: 2,
|
|
180
|
+
jitterFactor: 0,
|
|
181
|
+
onRetry,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const elapsedTime = Date.now() - startTime;
|
|
185
|
+
|
|
186
|
+
// First delay should be ~100ms, second should be ~200ms
|
|
187
|
+
// Total should be at least 300ms
|
|
188
|
+
expect(elapsedTime).toBeGreaterThanOrEqual(250);
|
|
189
|
+
expect(delays[0]).toBeGreaterThanOrEqual(90);
|
|
190
|
+
expect(delays[1]).toBeGreaterThanOrEqual(180);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it(
|
|
194
|
+
'should respect maxDelay cap',
|
|
195
|
+
async () => {
|
|
196
|
+
const operation = jest.fn().mockRejectedValue(new Error('Failure'));
|
|
197
|
+
|
|
198
|
+
const delays: number[] = [];
|
|
199
|
+
const onRetry = jest.fn((_error, _attempt, delay) => {
|
|
200
|
+
delays.push(delay);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
await retryWithBackoff(operation, {
|
|
205
|
+
maxAttempts: 5,
|
|
206
|
+
initialDelay: 1000,
|
|
207
|
+
maxDelay: 2000,
|
|
208
|
+
backoffMultiplier: 3,
|
|
209
|
+
jitterFactor: 0,
|
|
210
|
+
onRetry,
|
|
211
|
+
});
|
|
212
|
+
} catch (error) {
|
|
213
|
+
// Expected to fail
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// All delays should be capped at maxDelay
|
|
217
|
+
delays.forEach((delay) => {
|
|
218
|
+
expect(delay).toBeLessThanOrEqual(2000);
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
10000
|
|
222
|
+
); // 10 second timeout
|
|
223
|
+
|
|
224
|
+
it('should add jitter to delays', async () => {
|
|
225
|
+
const operation = jest
|
|
226
|
+
.fn()
|
|
227
|
+
.mockRejectedValueOnce(new Error('Failure 1'))
|
|
228
|
+
.mockRejectedValueOnce(new Error('Failure 2'))
|
|
229
|
+
.mockResolvedValue('success');
|
|
230
|
+
|
|
231
|
+
const delays: number[] = [];
|
|
232
|
+
const onRetry = jest.fn((_error, _attempt, delay) => {
|
|
233
|
+
delays.push(delay);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await retryWithBackoff(operation, {
|
|
237
|
+
maxAttempts: 3,
|
|
238
|
+
initialDelay: 1000,
|
|
239
|
+
backoffMultiplier: 1,
|
|
240
|
+
jitterFactor: 0.5, // 50% jitter
|
|
241
|
+
onRetry,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// With jitter, delays should vary around 1000ms
|
|
245
|
+
// Could be between 500ms and 1500ms
|
|
246
|
+
delays.forEach((delay) => {
|
|
247
|
+
expect(delay).toBeGreaterThanOrEqual(400);
|
|
248
|
+
expect(delay).toBeLessThanOrEqual(1600);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should handle non-Error objects', async () => {
|
|
253
|
+
const operation = jest.fn().mockRejectedValue('String error');
|
|
254
|
+
|
|
255
|
+
await expect(
|
|
256
|
+
retryWithBackoff(operation, {
|
|
257
|
+
maxAttempts: 2,
|
|
258
|
+
initialDelay: 10,
|
|
259
|
+
})
|
|
260
|
+
).rejects.toThrow('String error');
|
|
261
|
+
|
|
262
|
+
expect(operation).toHaveBeenCalledTimes(2);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe('retryable', () => {
|
|
267
|
+
it('should create retryable function that succeeds', async () => {
|
|
268
|
+
const originalFn = jest.fn((x: number) => Promise.resolve(x * 2));
|
|
269
|
+
const retryableFn = retryable(originalFn, { maxAttempts: 3 });
|
|
270
|
+
|
|
271
|
+
const result = await retryableFn(5);
|
|
272
|
+
|
|
273
|
+
expect(result).toBe(10);
|
|
274
|
+
expect(originalFn).toHaveBeenCalledWith(5);
|
|
275
|
+
expect(originalFn).toHaveBeenCalledTimes(1);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should create retryable function that retries on failure', async () => {
|
|
279
|
+
const originalFn = jest
|
|
280
|
+
.fn()
|
|
281
|
+
.mockRejectedValueOnce(new Error('Failure'))
|
|
282
|
+
.mockResolvedValue('success');
|
|
283
|
+
|
|
284
|
+
const retryableFn = retryable(originalFn, {
|
|
285
|
+
maxAttempts: 3,
|
|
286
|
+
initialDelay: 10,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const result = await retryableFn();
|
|
290
|
+
|
|
291
|
+
expect(result).toBe('success');
|
|
292
|
+
expect(originalFn).toHaveBeenCalledTimes(2);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should pass arguments to retried function', async () => {
|
|
296
|
+
const originalFn = jest.fn((a: number, b: string) =>
|
|
297
|
+
Promise.resolve(`${a}-${b}`)
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const retryableFn = retryable(originalFn);
|
|
301
|
+
|
|
302
|
+
const result = await retryableFn(42, 'test');
|
|
303
|
+
|
|
304
|
+
expect(result).toBe('42-test');
|
|
305
|
+
expect(originalFn).toHaveBeenCalledWith(42, 'test');
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
describe('LDAP_RETRY_OPTIONS', () => {
|
|
310
|
+
it('should have correct default values', () => {
|
|
311
|
+
expect(LDAP_RETRY_OPTIONS.maxAttempts).toBe(3);
|
|
312
|
+
expect(LDAP_RETRY_OPTIONS.initialDelay).toBe(1000);
|
|
313
|
+
expect(LDAP_RETRY_OPTIONS.maxDelay).toBe(10000);
|
|
314
|
+
expect(LDAP_RETRY_OPTIONS.backoffMultiplier).toBe(2);
|
|
315
|
+
expect(LDAP_RETRY_OPTIONS.jitterFactor).toBe(0.1);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('should use isRetryableError as shouldRetry', () => {
|
|
319
|
+
const retryableErr = new Error('timeout') as Error;
|
|
320
|
+
const nonRetryableErr = new Error('invalid credentials');
|
|
321
|
+
|
|
322
|
+
expect(LDAP_RETRY_OPTIONS.shouldRetry!(retryableErr, 0)).toBe(true);
|
|
323
|
+
expect(LDAP_RETRY_OPTIONS.shouldRetry!(nonRetryableErr, 0)).toBe(false);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should have onRetry callback', () => {
|
|
327
|
+
expect(LDAP_RETRY_OPTIONS.onRetry).toBeDefined();
|
|
328
|
+
expect(typeof LDAP_RETRY_OPTIONS.onRetry).toBe('function');
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe('Integration test with realistic scenario', () => {
|
|
333
|
+
it('should handle transient network failure followed by success', async () => {
|
|
334
|
+
// Simulate a flaky network connection
|
|
335
|
+
let attemptCount = 0;
|
|
336
|
+
const flakyOperation = async () => {
|
|
337
|
+
attemptCount++;
|
|
338
|
+
if (attemptCount < 3) {
|
|
339
|
+
const error = new Error('Network timeout') as Error & { code: string };
|
|
340
|
+
error.code = 'ETIMEDOUT';
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
return 'Connected successfully';
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const result = await retryWithBackoff(flakyOperation, {
|
|
347
|
+
maxAttempts: 3,
|
|
348
|
+
initialDelay: 10,
|
|
349
|
+
shouldRetry: isRetryableError,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(result).toBe('Connected successfully');
|
|
353
|
+
expect(attemptCount).toBe(3);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should immediately fail on authentication error', async () => {
|
|
357
|
+
const operation = async () => {
|
|
358
|
+
throw new Error('Invalid credentials');
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const startTime = Date.now();
|
|
362
|
+
|
|
363
|
+
await expect(
|
|
364
|
+
retryWithBackoff(operation, {
|
|
365
|
+
maxAttempts: 5,
|
|
366
|
+
initialDelay: 100,
|
|
367
|
+
shouldRetry: isRetryableError,
|
|
368
|
+
})
|
|
369
|
+
).rejects.toThrow('Invalid credentials');
|
|
370
|
+
|
|
371
|
+
const elapsedTime = Date.now() - startTime;
|
|
372
|
+
|
|
373
|
+
// Should fail immediately without retries (< 50ms)
|
|
374
|
+
expect(elapsedTime).toBeLessThan(50);
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
});
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { LDAPSanitizer } from '../../../../src/providers/ldap/ldap-sanitizer';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unit Tests for LDAP Sanitizer
|
|
5
|
+
* Task 8: Write Unit Tests for LDAP Provider (Story 1.5)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
describe('LDAPSanitizer', () => {
|
|
9
|
+
describe('sanitizeFilter', () => {
|
|
10
|
+
it('should escape backslash characters', () => {
|
|
11
|
+
const input = 'user\\name';
|
|
12
|
+
const result = LDAPSanitizer.sanitizeFilter(input);
|
|
13
|
+
expect(result).toBe('user\\5cname');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should escape asterisk characters', () => {
|
|
17
|
+
const input = 'user*';
|
|
18
|
+
const result = LDAPSanitizer.sanitizeFilter(input);
|
|
19
|
+
expect(result).toBe('user\\2a');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should escape parentheses', () => {
|
|
23
|
+
const input = 'user(name)';
|
|
24
|
+
const result = LDAPSanitizer.sanitizeFilter(input);
|
|
25
|
+
expect(result).toBe('user\\28name\\29');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should escape NUL character', () => {
|
|
29
|
+
const input = 'user\0name';
|
|
30
|
+
const result = LDAPSanitizer.sanitizeFilter(input);
|
|
31
|
+
expect(result).toBe('user\\00name');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should prevent LDAP injection with multiple special chars', () => {
|
|
35
|
+
const maliciousInput = '*)(objectClass=*)';
|
|
36
|
+
const result = LDAPSanitizer.sanitizeFilter(maliciousInput);
|
|
37
|
+
expect(result).toBe('\\2a\\29\\28objectClass=\\2a\\29');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should handle empty string', () => {
|
|
41
|
+
const result = LDAPSanitizer.sanitizeFilter('');
|
|
42
|
+
expect(result).toBe('');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should handle string with no special characters', () => {
|
|
46
|
+
const input = 'john.doe';
|
|
47
|
+
const result = LDAPSanitizer.sanitizeFilter(input);
|
|
48
|
+
expect(result).toBe('john.doe');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should escape backslash before other special chars to prevent double-escaping', () => {
|
|
52
|
+
const input = '\\*';
|
|
53
|
+
const result = LDAPSanitizer.sanitizeFilter(input);
|
|
54
|
+
expect(result).toBe('\\5c\\2a');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('sanitizeDN', () => {
|
|
59
|
+
it('should escape comma in DN value', () => {
|
|
60
|
+
const input = 'John, Doe';
|
|
61
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
62
|
+
expect(result).toBe('John\\, Doe');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should escape equals sign in value', () => {
|
|
66
|
+
const input = 'user=name';
|
|
67
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
68
|
+
expect(result).toBe('user\\=name');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should escape plus sign in value', () => {
|
|
72
|
+
const input = 'test+value';
|
|
73
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
74
|
+
expect(result).toBe('test\\+value');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should escape leading space', () => {
|
|
78
|
+
const input = ' leading';
|
|
79
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
80
|
+
expect(result).toBe('\\ leading');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should escape trailing space', () => {
|
|
84
|
+
const input = 'trailing ';
|
|
85
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
86
|
+
expect(result).toBe('trailing\\ ');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should escape leading hash', () => {
|
|
90
|
+
const input = '#hash';
|
|
91
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
92
|
+
expect(result).toBe('\\#hash');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should escape backslash in value', () => {
|
|
96
|
+
const input = 'test\\value';
|
|
97
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
98
|
+
expect(result).toBe('test\\\\value');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should handle empty string', () => {
|
|
102
|
+
const result = LDAPSanitizer.sanitizeDN('');
|
|
103
|
+
expect(result).toBe('');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle simple value with no special characters', () => {
|
|
107
|
+
const input = 'JohnDoe';
|
|
108
|
+
const result = LDAPSanitizer.sanitizeDN(input);
|
|
109
|
+
expect(result).toBe('JohnDoe');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('isValidFilter', () => {
|
|
114
|
+
it('should validate simple filter', () => {
|
|
115
|
+
const result = LDAPSanitizer.isValidFilter('(uid=john)');
|
|
116
|
+
expect(result).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should validate AND filter', () => {
|
|
120
|
+
const result = LDAPSanitizer.isValidFilter('(&(uid=john)(objectClass=person))');
|
|
121
|
+
expect(result).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should validate OR filter', () => {
|
|
125
|
+
const result = LDAPSanitizer.isValidFilter('(|(uid=john)(uid=jane))');
|
|
126
|
+
expect(result).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should validate NOT filter', () => {
|
|
130
|
+
const result = LDAPSanitizer.isValidFilter('(!(uid=admin))');
|
|
131
|
+
expect(result).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should reject filter without parentheses', () => {
|
|
135
|
+
const result = LDAPSanitizer.isValidFilter('uid=john');
|
|
136
|
+
expect(result).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should reject filter with unbalanced parentheses', () => {
|
|
140
|
+
const result = LDAPSanitizer.isValidFilter('(uid=john');
|
|
141
|
+
expect(result).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should reject filter with extra closing parenthesis', () => {
|
|
145
|
+
const result = LDAPSanitizer.isValidFilter('(uid=john))');
|
|
146
|
+
expect(result).toBe(false);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should reject empty string', () => {
|
|
150
|
+
const result = LDAPSanitizer.isValidFilter('');
|
|
151
|
+
expect(result).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should reject empty parentheses', () => {
|
|
155
|
+
const result = LDAPSanitizer.isValidFilter('()');
|
|
156
|
+
expect(result).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should validate complex nested filter', () => {
|
|
160
|
+
const result = LDAPSanitizer.isValidFilter(
|
|
161
|
+
'(&(objectClass=user)(|(uid=john)(mail=john@example.com)))'
|
|
162
|
+
);
|
|
163
|
+
expect(result).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('isValidDN', () => {
|
|
168
|
+
it('should validate simple DN', () => {
|
|
169
|
+
const result = LDAPSanitizer.isValidDN('cn=John Doe');
|
|
170
|
+
expect(result).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should validate full DN', () => {
|
|
174
|
+
const result = LDAPSanitizer.isValidDN('cn=John Doe,ou=Users,dc=example,dc=com');
|
|
175
|
+
expect(result).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('should validate DN with spaces', () => {
|
|
179
|
+
const result = LDAPSanitizer.isValidDN('cn=John Doe, ou=Users, dc=example, dc=com');
|
|
180
|
+
expect(result).toBe(true);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should reject invalid DN format', () => {
|
|
184
|
+
const result = LDAPSanitizer.isValidDN('invalid-dn');
|
|
185
|
+
expect(result).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should reject empty string', () => {
|
|
189
|
+
const result = LDAPSanitizer.isValidDN('');
|
|
190
|
+
expect(result).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should reject DN with invalid attribute name', () => {
|
|
194
|
+
const result = LDAPSanitizer.isValidDN('123invalid=value');
|
|
195
|
+
expect(result).toBe(false);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('sanitizeAttribute', () => {
|
|
200
|
+
it('should allow valid attribute names', () => {
|
|
201
|
+
const result = LDAPSanitizer.sanitizeAttribute('sAMAccountName');
|
|
202
|
+
expect(result).toBe('sAMAccountName');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should allow attributes with hyphens', () => {
|
|
206
|
+
const result = LDAPSanitizer.sanitizeAttribute('user-name');
|
|
207
|
+
expect(result).toBe('user-name');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('should allow attributes with dots', () => {
|
|
211
|
+
const result = LDAPSanitizer.sanitizeAttribute('user.name');
|
|
212
|
+
expect(result).toBe('user.name');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should remove special characters', () => {
|
|
216
|
+
const result = LDAPSanitizer.sanitizeAttribute('user@name!');
|
|
217
|
+
expect(result).toBe('username');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should handle empty string', () => {
|
|
221
|
+
const result = LDAPSanitizer.sanitizeAttribute('');
|
|
222
|
+
expect(result).toBe('');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('buildFilter', () => {
|
|
227
|
+
it('should build simple equality filter', () => {
|
|
228
|
+
const result = LDAPSanitizer.buildFilter('uid', '=', 'john');
|
|
229
|
+
expect(result).toBe('(uid=john)');
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should sanitize filter value', () => {
|
|
233
|
+
const result = LDAPSanitizer.buildFilter('uid', '=', 'john*');
|
|
234
|
+
expect(result).toBe('(uid=john\\2a)');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should build greater-than filter', () => {
|
|
238
|
+
const result = LDAPSanitizer.buildFilter('age', '>=', '18');
|
|
239
|
+
expect(result).toBe('(age>=18)');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should build less-than filter', () => {
|
|
243
|
+
const result = LDAPSanitizer.buildFilter('age', '<=', '65');
|
|
244
|
+
expect(result).toBe('(age<=65)');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should build approximate filter', () => {
|
|
248
|
+
const result = LDAPSanitizer.buildFilter('name', '~=', 'john');
|
|
249
|
+
expect(result).toBe('(name~=john)');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should allow wildcards with =* operator', () => {
|
|
253
|
+
const result = LDAPSanitizer.buildFilter('uid', '=*', '*john*');
|
|
254
|
+
expect(result).toBe('(uid=**john*)');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should sanitize attribute name', () => {
|
|
258
|
+
const result = LDAPSanitizer.buildFilter('user@name', '=', 'john');
|
|
259
|
+
expect(result).toBe('(username=john)');
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('buildLogicalFilter', () => {
|
|
264
|
+
it('should build AND filter', () => {
|
|
265
|
+
const result = LDAPSanitizer.buildLogicalFilter('&', [
|
|
266
|
+
'(uid=john)',
|
|
267
|
+
'(objectClass=person)',
|
|
268
|
+
]);
|
|
269
|
+
expect(result).toBe('(&(uid=john)(objectClass=person))');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should build OR filter', () => {
|
|
273
|
+
const result = LDAPSanitizer.buildLogicalFilter('|', ['(uid=john)', '(uid=jane)']);
|
|
274
|
+
expect(result).toBe('(|(uid=john)(uid=jane))');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should build NOT filter', () => {
|
|
278
|
+
const result = LDAPSanitizer.buildLogicalFilter('!', ['(uid=admin)']);
|
|
279
|
+
expect(result).toBe('(!(uid=admin))');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should handle empty array', () => {
|
|
283
|
+
const result = LDAPSanitizer.buildLogicalFilter('&', []);
|
|
284
|
+
expect(result).toBe('');
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should throw error for NOT with multiple filters', () => {
|
|
288
|
+
expect(() => {
|
|
289
|
+
LDAPSanitizer.buildLogicalFilter('!', ['(uid=john)', '(uid=jane)']);
|
|
290
|
+
}).toThrow('NOT operator requires exactly one filter');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should handle nested filters', () => {
|
|
294
|
+
const result = LDAPSanitizer.buildLogicalFilter('&', [
|
|
295
|
+
'(objectClass=user)',
|
|
296
|
+
'(|(uid=john)(mail=john@example.com))',
|
|
297
|
+
]);
|
|
298
|
+
expect(result).toBe('(&(objectClass=user)(|(uid=john)(mail=john@example.com)))');
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sample Test - Infrastructure Validation
|
|
3
|
+
* This test verifies that Jest and TypeScript are configured correctly
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
describe('Infrastructure Setup', () => {
|
|
7
|
+
it('should run Jest tests successfully', () => {
|
|
8
|
+
expect(1 + 1).toBe(2);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should support TypeScript', () => {
|
|
12
|
+
const sum = (a: number, b: number): number => a + b;
|
|
13
|
+
expect(sum(2, 3)).toBe(5);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should have test environment configured', () => {
|
|
17
|
+
expect(process.env['NODE_ENV']).toBe('test');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
File without changes
|