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 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-05 18:33:17 -06:00
parent 3b80e9c396
commit d53c80fef0
2 changed files with 248 additions and 2 deletions

View File

@@ -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;
}
});
});
});
});

View File

@@ -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<boolean>("orchestrator.yolo.enabled") ?? false;
const yoloRequested = this.configService.get<boolean>("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<string>("NODE_ENV") ?? process.env.NODE_ENV;
return nodeEnv === "production";
}
/**