Fix QA validation issues and add M7.1 security fixes (#318)
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #318.
This commit is contained in:
2026-02-04 03:08:09 +00:00
committed by jason.woltje
parent 482507ce4d
commit a1973e6419
178 changed files with 4902 additions and 74 deletions

View File

@@ -4,6 +4,7 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { ModuleRef } from "@nestjs/core";
import { HttpService } from "@nestjs/axios";
import { CommandService } from "./command.service";
import { PrismaService } from "../prisma/prisma.service";
@@ -16,6 +17,7 @@ import {
} from "@prisma/client";
import { of } from "rxjs";
import type { CommandMessage, CommandResponse } from "./types/message.types";
import { UnknownCommandTypeError } from "./errors/command.errors";
describe("CommandService", () => {
let service: CommandService;
@@ -23,6 +25,7 @@ describe("CommandService", () => {
let federationService: FederationService;
let signatureService: SignatureService;
let httpService: HttpService;
let moduleRef: ModuleRef;
const mockWorkspaceId = "workspace-123";
const mockConnectionId = "connection-123";
@@ -77,6 +80,7 @@ describe("CommandService", () => {
federationService = module.get<FederationService>(FederationService);
signatureService = module.get<SignatureService>(SignatureService);
httpService = module.get<HttpService>(HttpService);
moduleRef = module.get<ModuleRef>(ModuleRef);
});
describe("sendCommand", () => {
@@ -238,12 +242,75 @@ describe("CommandService", () => {
});
describe("handleIncomingCommand", () => {
it("should process a valid incoming command", async () => {
it("should process a valid incoming agent command", async () => {
const commandMessage: CommandMessage = {
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "spawn_agent",
payload: { agentType: "task_executor" },
commandType: "agent.spawn",
payload: { agentType: "task_executor", taskId: "task-123" },
timestamp: Date.now(),
signature: "signature-123",
};
const mockConnection = {
id: mockConnectionId,
remoteInstanceId: mockInstanceId,
status: FederationConnectionStatus.ACTIVE,
};
const mockIdentity = {
instanceId: "local-instance",
displayName: "Local Instance",
};
const mockFederationAgentService = {
handleAgentCommand: vi.fn().mockResolvedValue({
success: true,
data: { agentId: "agent-123", status: "spawning", spawnedAt: new Date().toISOString() },
}),
};
vi.spyOn(signatureService, "validateTimestamp").mockReturnValue(true);
vi.spyOn(prisma.federationConnection, "findFirst").mockResolvedValue(mockConnection as never);
vi.spyOn(signatureService, "verifyMessage").mockResolvedValue({
valid: true,
error: null,
} as never);
vi.spyOn(federationService, "getInstanceIdentity").mockResolvedValue(mockIdentity as never);
vi.spyOn(signatureService, "signMessage").mockResolvedValue("response-signature");
vi.spyOn(moduleRef, "get").mockReturnValue(mockFederationAgentService as never);
const response = await service.handleIncomingCommand(commandMessage);
expect(response).toMatchObject({
correlationId: "cmd-123",
instanceId: "local-instance",
success: true,
});
expect(signatureService.validateTimestamp).toHaveBeenCalledWith(commandMessage.timestamp);
expect(signatureService.verifyMessage).toHaveBeenCalledWith(
expect.objectContaining({
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "agent.spawn",
}),
"signature-123",
mockInstanceId
);
expect(mockFederationAgentService.handleAgentCommand).toHaveBeenCalledWith(
mockInstanceId,
"agent.spawn",
commandMessage.payload
);
});
it("should handle unknown command types and return error response", async () => {
const commandMessage: CommandMessage = {
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "unknown.command",
payload: {},
timestamp: Date.now(),
signature: "signature-123",
};
@@ -273,18 +340,125 @@ describe("CommandService", () => {
expect(response).toMatchObject({
correlationId: "cmd-123",
instanceId: "local-instance",
success: true,
success: false,
error: "Unknown command type: unknown.command",
});
});
expect(signatureService.validateTimestamp).toHaveBeenCalledWith(commandMessage.timestamp);
expect(signatureService.verifyMessage).toHaveBeenCalledWith(
expect.objectContaining({
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "spawn_agent",
it("should handle business logic errors from agent service and return error response", async () => {
const commandMessage: CommandMessage = {
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "agent.spawn",
payload: { agentType: "invalid_type" },
timestamp: Date.now(),
signature: "signature-123",
};
const mockConnection = {
id: mockConnectionId,
remoteInstanceId: mockInstanceId,
status: FederationConnectionStatus.ACTIVE,
};
const mockIdentity = {
instanceId: "local-instance",
displayName: "Local Instance",
};
vi.spyOn(signatureService, "validateTimestamp").mockReturnValue(true);
vi.spyOn(prisma.federationConnection, "findFirst").mockResolvedValue(mockConnection as never);
vi.spyOn(signatureService, "verifyMessage").mockResolvedValue({
valid: true,
error: null,
} as never);
vi.spyOn(federationService, "getInstanceIdentity").mockResolvedValue(mockIdentity as never);
vi.spyOn(signatureService, "signMessage").mockResolvedValue("response-signature");
// Mock FederationAgentService to return error response
const mockFederationAgentService = {
handleAgentCommand: vi.fn().mockResolvedValue({
success: false,
error: "Invalid agent type: invalid_type",
}),
"signature-123",
mockInstanceId
};
vi.spyOn(moduleRef, "get").mockReturnValue(mockFederationAgentService as never);
const response = await service.handleIncomingCommand(commandMessage);
expect(response).toMatchObject({
correlationId: "cmd-123",
instanceId: "local-instance",
success: false,
error: "Invalid agent type: invalid_type",
});
});
it("should let system errors propagate (database connection failure)", async () => {
const commandMessage: CommandMessage = {
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "agent.spawn",
payload: { agentType: "task_executor" },
timestamp: Date.now(),
signature: "signature-123",
};
vi.spyOn(signatureService, "validateTimestamp").mockReturnValue(true);
// Simulate database connection failure (system error)
const dbError = new Error("Connection pool exhausted");
dbError.name = "PoolExhaustedError";
vi.spyOn(prisma.federationConnection, "findFirst").mockRejectedValue(dbError);
// System errors should propagate
await expect(service.handleIncomingCommand(commandMessage)).rejects.toThrow(
"Connection pool exhausted"
);
});
it("should let system errors propagate from agent service (not wrapped)", async () => {
const commandMessage: CommandMessage = {
messageId: "cmd-123",
instanceId: mockInstanceId,
commandType: "agent.spawn",
payload: { agentType: "task_executor" },
timestamp: Date.now(),
signature: "signature-123",
};
const mockConnection = {
id: mockConnectionId,
remoteInstanceId: mockInstanceId,
status: FederationConnectionStatus.ACTIVE,
};
const mockIdentity = {
instanceId: "local-instance",
displayName: "Local Instance",
};
// Simulate a system error (not a CommandProcessingError) from agent service
const systemError = new Error("Database connection failed");
systemError.name = "DatabaseError";
const mockFederationAgentService = {
handleAgentCommand: vi.fn().mockRejectedValue(systemError),
};
vi.spyOn(signatureService, "validateTimestamp").mockReturnValue(true);
vi.spyOn(prisma.federationConnection, "findFirst").mockResolvedValue(mockConnection as never);
vi.spyOn(signatureService, "verifyMessage").mockResolvedValue({
valid: true,
error: null,
} as never);
vi.spyOn(federationService, "getInstanceIdentity").mockResolvedValue(mockIdentity as never);
vi.spyOn(moduleRef, "get").mockReturnValue(mockFederationAgentService as never);
// System errors should propagate (not caught by business logic handler)
await expect(service.handleIncomingCommand(commandMessage)).rejects.toThrow(
"Database connection failed"
);
});