Polish Deploy Recon page scan UX and Crucible cross-links.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

This commit is contained in:
AetherForge
2026-06-07 11:24:32 -07:00
parent f3e5a9a07d
commit 1560ca9489
14 changed files with 156 additions and 19 deletions

View File

@@ -1,6 +1,8 @@
/** @vitest-environment happy-dom */
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import CloudSpreadPanel from './CloudSpreadPanel';
vi.mock('../../api/client', () => ({
@@ -11,11 +13,28 @@ vi.mock('../../api/client', () => ({
}));
describe('CloudSpreadPanel', () => {
afterEach(() => cleanup());
it('renders cloud ecosystem hub', () => {
render(<CloudSpreadPanel serverUrl="https://deck.example" />);
render(
<MemoryRouter>
<CloudSpreadPanel serverUrl="https://deck.example" />
</MemoryRouter>,
);
expect(screen.getByTestId('cloud-spread-panel')).toBeInTheDocument();
expect(screen.getByTestId('cloud-method-s3-cloudfront')).toBeInTheDocument();
expect(screen.getByTestId('cloud-method-minio')).toBeInTheDocument();
});
it('shows Crucible cross-link when recon host is entered', async () => {
const user = userEvent.setup();
render(
<MemoryRouter>
<CloudSpreadPanel serverUrl="https://deck.example" />
</MemoryRouter>,
);
await user.type(screen.getAllByTestId('cloud-recon-host')[0], '10.1.2.50');
const link = screen.getByRole('link', { name: /Open Crucible spread tab/i });
expect(link).toHaveAttribute('href', '/crucible?reconHost=10.1.2.50&tab=spread&finding=ssm_document');
});
});

View File

@@ -1,5 +1,7 @@
import { memo, useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import { HelpTip } from '../HelpTip';
import { crucibleSpreadLink } from '../../help/deployRecon';
import { cloudMethodsByGroup, RELATED_SPREAD_TEMPLATE_LINKS } from '../../help/cloudSpreadMethods';
import CloudMethodPanel, { type CloudMethodConfig } from './CloudMethodPanel';
import './CloudSpreadPanel.css';
@@ -11,6 +13,7 @@ export interface CloudSpreadPanelProps {
}
function CloudSpreadPanelInner({ serverUrl, buildId = '', campaign = '' }: CloudSpreadPanelProps) {
const [reconHost, setReconHost] = useState('');
const [bucket, setBucket] = useState('aetherforge-shards');
const [cloudfrontDomain, setCloudfrontDomain] = useState('');
const [minioEndpoint, setMinioEndpoint] = useState('https://minio.example:9000');
@@ -64,6 +67,28 @@ function CloudSpreadPanelInner({ serverUrl, buildId = '', campaign = '' }: Cloud
{cloudMethodsByGroup('generic').map((m) => (
<CloudMethodPanel key={m.id} method={m} config={config} />
))}
<div className="cloud-spread-crucible-link">
<label className="cloud-spread-field">
<span className="label">
Recon host (no agent) <HelpTip field="ew_crucible_recon_link" />
</span>
<input
className="input mono"
placeholder="10.1.2.50 or ec2 internal IP"
value={reconHost}
onChange={(e) => setReconHost(e.target.value)}
data-testid="cloud-recon-host"
/>
</label>
{reconHost.trim() ? (
<p className="form-hint">
<Link to={crucibleSpreadLink(reconHost.trim(), 'ssm_document')}>Open Crucible spread tab</Link>
{' '} pre-fills deploy kit and spread-to-host for this target.
</p>
) : (
<p className="form-hint">Paste a recon IP from service graph or vuln findings to continue in Crucible.</p>
)}
</div>
<details className="cloud-spread-related">
<summary className="font-tech">Related lateral templates</summary>
<ul>