Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/component-sdk/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ async function runDockerWithStandardIO<I, O>(
});

reject(new ContainerError(`Docker container failed with exit code ${code}: ${stderr}`, {
details: { exitCode: code, stderr, dockerArgs: formatArgs(dockerArgs) },
details: { exitCode: code, stderr, stdout, dockerArgs: formatArgs(dockerArgs) },
}));
return;
}
Expand Down Expand Up @@ -461,6 +461,7 @@ async function runDockerWithPty<I, O>(
{
details: {
exitCode,
stdout,
dockerArgs: formatArgs(dockerArgs),
},
},
Expand Down
74 changes: 23 additions & 51 deletions worker/src/components/security/__tests__/amass.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,24 @@ describe('amass component', () => {

const parsedParams = component.parameters!.parse(paramValues);

expect(parsedParams.passive).toBe(true);
expect(parsedParams.active).toBe(false);
expect(parsedParams.bruteForce).toBe(false);
expect(parsedParams.includeIps).toBe(false);
expect(parsedParams.enableAlterations).toBe(false);
expect(parsedParams.recursive).toBe(true);
expect(parsedParams.recursive).toBe(false);
expect(parsedParams.verbose).toBe(false);
expect(parsedParams.demoMode).toBe(false);
expect(parsedParams.timeoutMinutes).toBeUndefined();
expect(parsedParams.timeoutMinutes).toBe(15);
expect(parsedParams.resolvers).toBe('1.1.1.1,8.8.8.8,9.9.9.9,8.8.4.4,1.0.0.1');
expect(parsedParams.dataSources).toBe('crtsh,hackertarget');
expect(parsedParams.minForRecursive).toBeUndefined();
expect(parsedParams.maxDepth).toBeUndefined();
expect(parsedParams.dnsQueryRate).toBeUndefined();
expect(parsedParams.customFlags).toBeUndefined();
});

it('should parse raw JSON response returned as string', async () => {
it('should parse raw text output from docker container', async () => {
const component = componentRegistry.get<AmassInput, AmassOutput>('shipsec.amass.enum');
if (!component) throw new Error('Component not registered');

Expand All @@ -59,35 +62,18 @@ describe('amass component', () => {
},
};

const payload = JSON.stringify({
subdomains: ['api.example.com'],
rawOutput: 'api.example.com',
domainCount: 1,
subdomainCount: 1,
options: {
active: true,
bruteForce: false,
includeIps: false,
enableAlterations: false,
recursive: true,
verbose: false,
demoMode: false,
timeoutMinutes: null,
minForRecursive: null,
maxDepth: null,
dnsQueryRate: null,
customFlags: null,
},
});

vi.spyOn(sdk, 'runComponentWithRunner').mockResolvedValue(payload);
// Mock docker returning raw subdomain output (one per line)
vi.spyOn(sdk, 'runComponentWithRunner').mockResolvedValue('api.example.com\nwww.example.com');

const result = await component.execute(executePayload, context);

expect(result).toEqual(component.outputs.parse(JSON.parse(payload)));
expect(result.subdomains).toContain('api.example.com');
expect(result.subdomains).toContain('www.example.com');
expect(result.subdomainCount).toBe(2);
expect(result.domainCount).toBe(1);
});

it('should propagate structured output when docker returns JSON', async () => {
it('should handle structured object output from docker', async () => {
const component = componentRegistry.get<AmassInput, AmassOutput>('shipsec.amass.enum');
if (!component) throw new Error('Component not registered');

Expand All @@ -107,31 +93,17 @@ describe('amass component', () => {
},
};

const payload = component.outputs.parse({
subdomains: ['login.example.com', 'dev.example.org'],
rawOutput: 'login.example.com\nlogin.example.com 93.184.216.34\ndev.example.org',
domainCount: 2,
subdomainCount: 2,
options: {
active: false,
bruteForce: true,
includeIps: true,
enableAlterations: false,
recursive: true,
verbose: false,
demoMode: false,
timeoutMinutes: 2,
minForRecursive: null,
maxDepth: null,
dnsQueryRate: null,
customFlags: null,
},
});

vi.spyOn(sdk, 'runComponentWithRunner').mockResolvedValue(payload);
// Mock docker returning raw output with IP addresses
const rawOutput = 'login.example.com 93.184.216.34\ndev.example.org';
vi.spyOn(sdk, 'runComponentWithRunner').mockResolvedValue(rawOutput);

const result = await component.execute(executePayload, context);
expect(result).toEqual(payload);

expect(result.subdomains).toContain('login.example.com');
expect(result.subdomains).toContain('dev.example.org');
expect(result.subdomainCount).toBe(2);
expect(result.domainCount).toBe(2);
expect(result.rawOutput).toBe(rawOutput);
});

it('should configure docker runner for owaspamass/amass image', () => {
Expand All @@ -140,7 +112,7 @@ describe('amass component', () => {

expect(component.runner.kind).toBe('docker');
if (component.runner.kind === 'docker') {
expect(component.runner.image).toBe('owaspamass/amass:v4.2.0');
expect(component.runner.image).toBe('owaspamass/amass:v5.0.1');
expect(component.runner.entrypoint).toBe('sh');
expect(component.runner.command).toBeInstanceOf(Array);
}
Expand Down
18 changes: 13 additions & 5 deletions worker/src/components/security/__tests__/subfinder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ describe('subfinder component', () => {
expect(params.domain).toBe('legacy.example.com');
});

it('should inject provider config content into docker environment when configured', async () => {
it('should pass provider config via -pc flag when configured', async () => {
const component = componentRegistry.get<SubfinderInput, SubfinderOutput>(
'shipsec.subfinder.run',
);
Expand Down Expand Up @@ -137,9 +137,17 @@ describe('subfinder component', () => {
const [runnerConfig] = runnerSpy.mock.calls[0];
expect(runnerConfig).toBeDefined();
if (runnerConfig && runnerConfig.kind === 'docker') {
expect(runnerConfig.env?.SUBFINDER_PROVIDER_CONFIG_B64).toBe(
Buffer.from(secretValue, 'utf8').toString('base64'),
);
// After Dynamic Args Pattern refactoring, provider config is mounted as a file
// and passed via -pc flag in the command arguments
const command = runnerConfig.command ?? [];
expect(command).toContain('-pc');
// The -pc flag should be followed by the path to the provider config file
const pcIndex = command.indexOf('-pc');
expect(pcIndex).toBeGreaterThan(-1);
expect(command[pcIndex + 1]).toContain('provider-config.yaml');
// Volume should be configured
expect(runnerConfig.volumes).toBeDefined();
expect(runnerConfig.volumes?.length).toBeGreaterThan(0);
}
});

Expand All @@ -151,7 +159,7 @@ describe('subfinder component', () => {

expect(component.runner.kind).toBe('docker');
if (component.runner.kind === 'docker') {
expect(component.runner.image).toBe('projectdiscovery/subfinder:v2.10.1');
expect(component.runner.image).toBe('projectdiscovery/subfinder:v2.12.0');
}
});
});
Loading