From d53c80fef0be7809bda654f56bb7cda1c570c9b6 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 5 Feb 2026 18:33:17 -0600 Subject: [PATCH] fix(#338): Block YOLO mode in production - Add isProductionEnvironment() check to prevent YOLO mode bypass - Log warning when YOLO mode request is blocked in production - Fall back to process.env.NODE_ENV when config service returns undefined - Add comprehensive tests for production blocking behavior SECURITY: YOLO mode bypasses all quality gates which is dangerous in production environments. This change ensures quality gates are always enforced when NODE_ENV=production. Refs #338 Co-Authored-By: Claude Opus 4.5 --- .../coordinator/quality-gates.service.spec.ts | 217 ++++++++++++++++++ .../src/coordinator/quality-gates.service.ts | 33 ++- 2 files changed, 248 insertions(+), 2 deletions(-) diff --git a/apps/orchestrator/src/coordinator/quality-gates.service.spec.ts b/apps/orchestrator/src/coordinator/quality-gates.service.spec.ts index 9e67830..9b7067e 100644 --- a/apps/orchestrator/src/coordinator/quality-gates.service.spec.ts +++ b/apps/orchestrator/src/coordinator/quality-gates.service.spec.ts @@ -1288,5 +1288,222 @@ describe("QualityGatesService", () => { }); }); }); + + describe("YOLO mode blocked in production (SEC-ORCH-13)", () => { + const params = { + taskId: "task-prod-123", + agentId: "agent-prod-456", + files: ["src/feature.ts"], + diffSummary: "Production deployment", + }; + + it("should block YOLO mode when NODE_ENV is production", async () => { + // Enable YOLO mode but set production environment + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return "production"; + } + return undefined; + }); + + const mockResponse: QualityCheckResponse = { + approved: true, + gate: "pre-commit", + message: "All checks passed", + }; + + vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse); + + const result = await service.preCommitCheck(params); + + // Should call coordinator (YOLO mode blocked in production) + expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled(); + + // Should return coordinator response, not YOLO bypass + expect(result.approved).toBe(true); + expect(result.message).toBe("All checks passed"); + expect(result.details?.yoloMode).toBeUndefined(); + }); + + it("should log warning when YOLO mode is blocked in production", async () => { + // Enable YOLO mode but set production environment + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return "production"; + } + return undefined; + }); + + const loggerWarnSpy = vi.spyOn(service["logger"], "warn"); + + const mockResponse: QualityCheckResponse = { + approved: true, + gate: "pre-commit", + }; + + vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse); + + await service.preCommitCheck(params); + + // Should log warning about YOLO mode being blocked + expect(loggerWarnSpy).toHaveBeenCalledWith( + "YOLO mode blocked in production environment - quality gates will be enforced", + expect.objectContaining({ + requestedYoloMode: true, + environment: "production", + }) + ); + }); + + it("should allow YOLO mode in development environment", async () => { + // Enable YOLO mode with development environment + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return "development"; + } + return undefined; + }); + + const result = await service.preCommitCheck(params); + + // Should NOT call coordinator (YOLO mode enabled) + expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled(); + + // Should return YOLO bypass result + expect(result.approved).toBe(true); + expect(result.message).toBe("Quality gates disabled (YOLO mode)"); + expect(result.details?.yoloMode).toBe(true); + }); + + it("should allow YOLO mode in test environment", async () => { + // Enable YOLO mode with test environment + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return "test"; + } + return undefined; + }); + + const result = await service.postCommitCheck(params); + + // Should NOT call coordinator (YOLO mode enabled) + expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled(); + + // Should return YOLO bypass result + expect(result.approved).toBe(true); + expect(result.message).toBe("Quality gates disabled (YOLO mode)"); + expect(result.details?.yoloMode).toBe(true); + }); + + it("should block YOLO mode for post-commit in production", async () => { + // Enable YOLO mode but set production environment + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return "production"; + } + return undefined; + }); + + const mockResponse: QualityCheckResponse = { + approved: false, + gate: "post-commit", + message: "Coverage below threshold", + details: { + coverage: { current: 78, required: 85 }, + }, + }; + + vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse); + + const result = await service.postCommitCheck(params); + + // Should call coordinator and enforce quality gates + expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled(); + + // Should return coordinator rejection, not YOLO bypass + expect(result.approved).toBe(false); + expect(result.message).toBe("Coverage below threshold"); + expect(result.details?.coverage).toEqual({ current: 78, required: 85 }); + }); + + it("should work when NODE_ENV is not set (default to non-production)", async () => { + // Enable YOLO mode without NODE_ENV set + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return undefined; + } + return undefined; + }); + + // Also clear process.env.NODE_ENV + const originalNodeEnv = process.env.NODE_ENV; + delete process.env.NODE_ENV; + + try { + const result = await service.preCommitCheck(params); + + // Should allow YOLO mode when NODE_ENV not set + expect(mockCoordinatorClient.checkQuality).not.toHaveBeenCalled(); + expect(result.approved).toBe(true); + expect(result.details?.yoloMode).toBe(true); + } finally { + // Restore NODE_ENV + process.env.NODE_ENV = originalNodeEnv; + } + }); + + it("should fall back to process.env.NODE_ENV when config not set", async () => { + // Enable YOLO mode, config returns undefined but process.env is production + vi.mocked(mockConfigService.get).mockImplementation((key: string) => { + if (key === "orchestrator.yolo.enabled") { + return true; + } + if (key === "NODE_ENV") { + return undefined; + } + return undefined; + }); + + // Set process.env.NODE_ENV to production + const originalNodeEnv = process.env.NODE_ENV; + process.env.NODE_ENV = "production"; + + try { + const mockResponse: QualityCheckResponse = { + approved: true, + gate: "pre-commit", + }; + + vi.mocked(mockCoordinatorClient.checkQuality).mockResolvedValueOnce(mockResponse); + + const result = await service.preCommitCheck(params); + + // Should block YOLO mode (production via process.env) + expect(mockCoordinatorClient.checkQuality).toHaveBeenCalled(); + expect(result.details?.yoloMode).toBeUndefined(); + } finally { + // Restore NODE_ENV + process.env.NODE_ENV = originalNodeEnv; + } + }); + }); }); }); diff --git a/apps/orchestrator/src/coordinator/quality-gates.service.ts b/apps/orchestrator/src/coordinator/quality-gates.service.ts index 2bf7cbf..561e0e4 100644 --- a/apps/orchestrator/src/coordinator/quality-gates.service.ts +++ b/apps/orchestrator/src/coordinator/quality-gates.service.ts @@ -217,10 +217,39 @@ export class QualityGatesService { * YOLO mode bypasses all quality gates. * Default: false (quality gates enabled) * - * @returns True if YOLO mode is enabled + * SECURITY: YOLO mode is blocked in production environments to prevent + * bypassing quality gates in production deployments. This is a security + * measure to ensure code quality standards are always enforced in production. + * + * @returns True if YOLO mode is enabled (always false in production) */ private isYoloModeEnabled(): boolean { - return this.configService.get("orchestrator.yolo.enabled") ?? false; + const yoloRequested = this.configService.get("orchestrator.yolo.enabled") ?? false; + + // Block YOLO mode in production + if (yoloRequested && this.isProductionEnvironment()) { + this.logger.warn( + "YOLO mode blocked in production environment - quality gates will be enforced", + { + requestedYoloMode: true, + environment: "production", + timestamp: new Date().toISOString(), + } + ); + return false; + } + + return yoloRequested; + } + + /** + * Check if running in production environment + * + * @returns True if NODE_ENV is 'production' + */ + private isProductionEnvironment(): boolean { + const nodeEnv = this.configService.get("NODE_ENV") ?? process.env.NODE_ENV; + return nodeEnv === "production"; } /**