import { beforeEach, describe, expect, it, vi } from "vitest";
import { ComposeService } from "./compose.js";
import type { ISSHService } from "./interfaces.js";
describe("Compose Security - Integration", () => {
let composeService: ComposeService;
let mockSSHService: ISSHService;
beforeEach(() => {
mockSSHService = {
executeSSHCommand: vi.fn(),
getHostResources: vi.fn() as never,
};
composeService = new ComposeService(mockSSHService);
});
it("should prevent command injection through entire call chain", async () => {
const maliciousHost = {
name: "test-host",
host: "localhost",
protocol: "http" as const,
port: 2375,
};
// Attempt realistic attack: stop legitimate service, then delete data
const attackVector = ["down", "-v;", "rm", "-rf", "/var/lib/docker"];
// After S-M1 fix: Arguments are escaped rather than rejected
// The test should verify that dangerous characters are neutralized via escaping
// Mock should verify the escaped command is safe
vi.mocked(mockSSHService.executeSSHCommand).mockResolvedValue("mocked output");
await composeService.composeExec(maliciousHost, "production-db", "up", attackVector);
// Verify that escapeShellArg was applied to dangerous arguments
// The semicolon should be escaped/quoted so it can't be used for command injection
const calls = vi.mocked(mockSSHService.executeSSHCommand).mock.calls;
expect(calls.length).toBeGreaterThan(0);
// Check that the command string contains properly escaped arguments
// For SSH execution, buildComposeCommand creates a space-joined command string (first arg)
const commandString = calls[0][1]; // second argument is the command string
expect(commandString).toBeDefined();
// Verify dangerous arguments are escaped (shell escaping wraps them in single quotes)
// escapeShellArg wraps args in single quotes: '-v;' becomes '\'-v;\''
expect(commandString).toContain("'-v;'"); // Semicolon escaped
expect(commandString).toContain("'down'"); // 'down' escaped
expect(commandString).toContain("'rm'"); // 'rm' escaped
});
});