Security Remediation: All Phases Complete (84 fixes) #348
@@ -3,7 +3,6 @@ import { QueueService } from "../../queue/queue.service";
|
||||
import { AgentSpawnerService } from "../../spawner/agent-spawner.service";
|
||||
import { AgentLifecycleService } from "../../spawner/agent-lifecycle.service";
|
||||
import { KillswitchService } from "../../killswitch/killswitch.service";
|
||||
import { BadRequestException } from "@nestjs/common";
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
|
||||
describe("AgentsController", () => {
|
||||
@@ -289,80 +288,6 @@ describe("AgentsController", () => {
|
||||
expect(result.agentId).toBe(agentId);
|
||||
});
|
||||
|
||||
it("should throw BadRequestException when taskId is missing", async () => {
|
||||
// Arrange
|
||||
const invalidRequest = {
|
||||
agentType: "worker" as const,
|
||||
context: validRequest.context,
|
||||
} as unknown as typeof validRequest;
|
||||
|
||||
// Act & Assert
|
||||
await expect(controller.spawn(invalidRequest)).rejects.toThrow(BadRequestException);
|
||||
expect(spawnerService.spawnAgent).not.toHaveBeenCalled();
|
||||
expect(queueService.addTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw BadRequestException when agentType is invalid", async () => {
|
||||
// Arrange
|
||||
const invalidRequest = {
|
||||
...validRequest,
|
||||
agentType: "invalid" as unknown as "worker",
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await expect(controller.spawn(invalidRequest)).rejects.toThrow(BadRequestException);
|
||||
expect(spawnerService.spawnAgent).not.toHaveBeenCalled();
|
||||
expect(queueService.addTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw BadRequestException when repository is missing", async () => {
|
||||
// Arrange
|
||||
const invalidRequest = {
|
||||
...validRequest,
|
||||
context: {
|
||||
...validRequest.context,
|
||||
repository: "",
|
||||
},
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await expect(controller.spawn(invalidRequest)).rejects.toThrow(BadRequestException);
|
||||
expect(spawnerService.spawnAgent).not.toHaveBeenCalled();
|
||||
expect(queueService.addTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw BadRequestException when branch is missing", async () => {
|
||||
// Arrange
|
||||
const invalidRequest = {
|
||||
...validRequest,
|
||||
context: {
|
||||
...validRequest.context,
|
||||
branch: "",
|
||||
},
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await expect(controller.spawn(invalidRequest)).rejects.toThrow(BadRequestException);
|
||||
expect(spawnerService.spawnAgent).not.toHaveBeenCalled();
|
||||
expect(queueService.addTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should throw BadRequestException when workItems is empty", async () => {
|
||||
// Arrange
|
||||
const invalidRequest = {
|
||||
...validRequest,
|
||||
context: {
|
||||
...validRequest.context,
|
||||
workItems: [],
|
||||
},
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
await expect(controller.spawn(invalidRequest)).rejects.toThrow(BadRequestException);
|
||||
expect(spawnerService.spawnAgent).not.toHaveBeenCalled();
|
||||
expect(queueService.addTask).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should propagate errors from spawner service", async () => {
|
||||
// Arrange
|
||||
const error = new Error("Spawner failed");
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Get,
|
||||
Body,
|
||||
Param,
|
||||
BadRequestException,
|
||||
NotFoundException,
|
||||
Logger,
|
||||
UsePipes,
|
||||
@@ -57,8 +56,9 @@ export class AgentsController {
|
||||
this.logger.log(`Received spawn request for task: ${dto.taskId}`);
|
||||
|
||||
try {
|
||||
// Validate request manually (in addition to ValidationPipe)
|
||||
this.validateSpawnRequest(dto);
|
||||
// Validation is handled by:
|
||||
// 1. ValidationPipe + DTO decorators at the HTTP layer
|
||||
// 2. AgentSpawnerService.validateSpawnRequest for business logic
|
||||
|
||||
// Spawn agent using spawner service
|
||||
const spawnResponse = this.spawnerService.spawnAgent({
|
||||
@@ -243,32 +243,4 @@ export class AgentsController {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate spawn request
|
||||
* @param dto Spawn request to validate
|
||||
* @throws BadRequestException if validation fails
|
||||
*/
|
||||
private validateSpawnRequest(dto: SpawnAgentDto): void {
|
||||
if (!dto.taskId || dto.taskId.trim() === "") {
|
||||
throw new BadRequestException("taskId is required");
|
||||
}
|
||||
|
||||
const validAgentTypes = ["worker", "reviewer", "tester"];
|
||||
if (!validAgentTypes.includes(dto.agentType)) {
|
||||
throw new BadRequestException(`agentType must be one of: ${validAgentTypes.join(", ")}`);
|
||||
}
|
||||
|
||||
if (!dto.context.repository || dto.context.repository.trim() === "") {
|
||||
throw new BadRequestException("context.repository is required");
|
||||
}
|
||||
|
||||
if (!dto.context.branch || dto.context.branch.trim() === "") {
|
||||
throw new BadRequestException("context.branch is required");
|
||||
}
|
||||
|
||||
if (dto.context.workItems.length === 0) {
|
||||
throw new BadRequestException("context.workItems must not be empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user