diff --git a/docs/advanced/skills.mdx b/docs/advanced/skills.mdx index 5345600..38aacd0 100644 --- a/docs/advanced/skills.mdx +++ b/docs/advanced/skills.mdx @@ -81,6 +81,21 @@ Protocol-specific testing techniques. | --------- | ------------------------------------------------ | | `graphql` | GraphQL introspection, batching, resolver issues | +### Tooling + +Sandbox CLI playbooks for core recon and scanning tools. + +| Skill | Coverage | +| ----------- | ------------------------------------------------------- | +| `nmap` | Port/service scan syntax and high-signal scan patterns | +| `nuclei` | Template selection, severity filtering, and rate tuning | +| `httpx` | HTTP probing and fingerprint output patterns | +| `ffuf` | Wordlist fuzzing, matcher/filter strategy, recursion | +| `subfinder` | Passive subdomain enumeration and source control | +| `naabu` | Fast port scanning with explicit rate/verify controls | +| `katana` | Crawl depth/JS/known-files behavior and pitfalls | +| `sqlmap` | SQLi workflow for enumeration and controlled extraction | + ## Skill Structure Each skill is a Markdown file with YAML frontmatter for metadata: diff --git a/strix/interface/tool_components/__init__.py b/strix/interface/tool_components/__init__.py index cb8aeea..c8b6007 100644 --- a/strix/interface/tool_components/__init__.py +++ b/strix/interface/tool_components/__init__.py @@ -4,6 +4,7 @@ from . import ( browser_renderer, file_edit_renderer, finish_renderer, + load_skill_renderer, notes_renderer, proxy_renderer, python_renderer, @@ -28,6 +29,7 @@ __all__ = [ "file_edit_renderer", "finish_renderer", "get_tool_renderer", + "load_skill_renderer", "notes_renderer", "proxy_renderer", "python_renderer", diff --git a/strix/interface/tool_components/load_skill_renderer.py b/strix/interface/tool_components/load_skill_renderer.py new file mode 100644 index 0000000..41a1868 --- /dev/null +++ b/strix/interface/tool_components/load_skill_renderer.py @@ -0,0 +1,33 @@ +from typing import Any, ClassVar + +from rich.text import Text +from textual.widgets import Static + +from .base_renderer import BaseToolRenderer +from .registry import register_tool_renderer + + +@register_tool_renderer +class LoadSkillRenderer(BaseToolRenderer): + tool_name: ClassVar[str] = "load_skill" + css_classes: ClassVar[list[str]] = ["tool-call", "load-skill-tool"] + + @classmethod + def render(cls, tool_data: dict[str, Any]) -> Static: + args = tool_data.get("args", {}) + status = tool_data.get("status", "completed") + + requested = args.get("skills", "") + + text = Text() + text.append("◇ ", style="#10b981") + text.append("loading skill", style="dim") + + if requested: + text.append(" ") + text.append(requested, style="#10b981") + elif not tool_data.get("result"): + text.append("\n ") + text.append("Loading...", style="dim") + + return Static(text, classes=cls.get_css_classes(status)) diff --git a/strix/llm/llm.py b/strix/llm/llm.py index 1091f3b..fe1758f 100644 --- a/strix/llm/llm.py +++ b/strix/llm/llm.py @@ -63,6 +63,7 @@ class LLM: self.config = config self.agent_name = agent_name self.agent_id: str | None = None + self._active_skills: list[str] = list(config.skills or []) self._total_stats = RequestStats() self.memory_compressor = MemoryCompressor(model_name=config.litellm_model) self.system_prompt = self._load_system_prompt(agent_name) @@ -87,10 +88,7 @@ class LLM: autoescape=select_autoescape(enabled_extensions=(), default_for_string=False), ) - skills_to_load = [ - *list(self.config.skills or []), - f"scan_modes/{self.config.scan_mode}", - ] + skills_to_load = self._get_skills_to_load() skill_content = load_skills(skills_to_load) env.globals["get_skill"] = lambda name: skill_content.get(name, "") @@ -104,6 +102,36 @@ class LLM: except Exception: # noqa: BLE001 return "" + def _get_skills_to_load(self) -> list[str]: + ordered_skills = [*self._active_skills] + ordered_skills.append(f"scan_modes/{self.config.scan_mode}") + + deduped: list[str] = [] + seen: set[str] = set() + for skill_name in ordered_skills: + if skill_name not in seen: + deduped.append(skill_name) + seen.add(skill_name) + + return deduped + + def add_skills(self, skill_names: list[str]) -> list[str]: + added: list[str] = [] + for skill_name in skill_names: + if not skill_name or skill_name in self._active_skills: + continue + self._active_skills.append(skill_name) + added.append(skill_name) + + if not added: + return [] + + updated_prompt = self._load_system_prompt(self.agent_name) + if updated_prompt: + self.system_prompt = updated_prompt + + return added + def set_agent_identity(self, agent_name: str | None, agent_id: str | None) -> None: if agent_name: self.agent_name = agent_name diff --git a/strix/skills/README.md b/strix/skills/README.md index 4543cd5..1d4f71d 100644 --- a/strix/skills/README.md +++ b/strix/skills/README.md @@ -33,6 +33,7 @@ The skills are dynamically injected into the agent's system prompt, allowing it | **`/frameworks`** | Specific testing methods for popular frameworks e.g. Django, Express, FastAPI, and Next.js | | **`/technologies`** | Specialized techniques for third-party services such as Supabase, Firebase, Auth0, and payment gateways | | **`/protocols`** | Protocol-specific testing patterns for GraphQL, WebSocket, OAuth, and other communication standards | +| **`/tooling`** | Command-line playbooks for core sandbox tools (nmap, nuclei, httpx, ffuf, subfinder, naabu, katana, sqlmap) | | **`/cloud`** | Cloud provider security testing for AWS, Azure, GCP, and Kubernetes environments | | **`/reconnaissance`** | Advanced information gathering and enumeration techniques for comprehensive attack surface mapping | | **`/custom`** | Community-contributed skills for specialized or industry-specific testing scenarios | diff --git a/strix/skills/__init__.py b/strix/skills/__init__.py index c9cdf03..37ffc58 100644 --- a/strix/skills/__init__.py +++ b/strix/skills/__init__.py @@ -54,6 +54,30 @@ def validate_skill_names(skill_names: list[str]) -> dict[str, list[str]]: return {"valid": valid_skills, "invalid": invalid_skills} +def parse_skill_list(skills: str | None) -> list[str]: + if not skills: + return [] + return [s.strip() for s in skills.split(",") if s.strip()] + + +def validate_requested_skills(skill_list: list[str], max_skills: int = 5) -> str | None: + if len(skill_list) > max_skills: + return "Cannot specify more than 5 skills for an agent (use comma-separated format)" + + if not skill_list: + return None + + validation = validate_skill_names(skill_list) + if validation["invalid"]: + available_skills = list(get_all_skill_names()) + return ( + f"Invalid skills: {validation['invalid']}. " + f"Available skills: {', '.join(available_skills)}" + ) + + return None + + def generate_skills_description() -> str: available_skills = get_available_skills() diff --git a/strix/skills/tooling/ffuf.md b/strix/skills/tooling/ffuf.md new file mode 100644 index 0000000..0c4d1f0 --- /dev/null +++ b/strix/skills/tooling/ffuf.md @@ -0,0 +1,66 @@ +--- +name: ffuf +description: ffuf fuzzing syntax with matcher/filter strategy and non-interactive defaults. +--- + +# ffuf CLI Playbook + +Official docs: +- https://github.com/ffuf/ffuf + +Canonical syntax: +`ffuf -w -u [flags]` + +High-signal flags: +- `-u ` target URL containing `FUZZ` +- `-w ` wordlist input (supports `KEYWORD` mapping via `-w file:KEYWORD`) +- `-mc ` match status codes +- `-fc ` filter status codes +- `-fs ` filter by body size +- `-ac` auto-calibration +- `-t ` threads +- `-rate ` request rate +- `-timeout ` HTTP timeout +- `-x ` upstream proxy (HTTP/SOCKS) +- `-ignore-body` skip downloading response body +- `-noninteractive` disable interactive console mode +- `-recursion` and `-recursion-depth ` recursive discovery +- `-H
` custom headers +- `-X ` and `-d ` for non-GET fuzzing +- `-o -of ` structured output + +Agent-safe baseline for automation: +`ffuf -w wordlist.txt -u https://target.tld/FUZZ -mc 200,204,301,302,307,401,403,405 -ac -t 20 -rate 50 -timeout 10 -noninteractive -of json -o ffuf.json` + +Common patterns: +- Basic path fuzzing: + `ffuf -w /path/wordlist.txt -u https://target.tld/FUZZ -mc 200,204,301,302,307,401,403 -ac -t 40 -rate 200 -noninteractive` +- Vhost fuzzing: + `ffuf -w vhosts.txt -u https://target.tld -H 'Host: FUZZ.target.tld' -fs 0 -ac -noninteractive` +- Parameter value fuzzing: + `ffuf -w values.txt -u 'https://target.tld/search?q=FUZZ' -mc all -fs 0 -ac -t 30 -noninteractive` +- POST body fuzzing: + `ffuf -w payloads.txt -u https://target.tld/login -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'username=admin&password=FUZZ' -fc 401 -noninteractive` +- Recursive discovery: + `ffuf -w dirs.txt -u https://target.tld/FUZZ -recursion -recursion-depth 2 -ac -t 30 -noninteractive` +- Proxy-instrumented run: + `ffuf -w wordlist.txt -u https://target.tld/FUZZ -x http://127.0.0.1:48080 -mc 200,301,302,403 -ac -noninteractive` + +Critical correctness rules: +- `FUZZ` must appear exactly at the mutation point in URL/header/body. +- If using `-w file:KEYWORD`, that same `KEYWORD` must be present in URL/header/body. +- Always include `-noninteractive` in agent/script execution to prevent ffuf console mode from swallowing subsequent shell commands. +- Save structured output with `-of json -o ` for deterministic parsing. + +Usage rules: +- Prefer explicit matcher/filter strategy (`-mc`/`-fc`/`-fs`) over default-only output. +- Start conservative (`-rate`, `-t`) and scale only if target tolerance is known. +- Do not use `-h`/`--help` during normal execution unless absolutely necessary. + +Failure recovery: +- If ffuf drops into interactive mode, send `C-c` and rerun with `-noninteractive`. +- If response noise is too high, tighten `-mc/-fc/-fs` instead of increasing load. +- If runtime is too long, lower `-rate/-t` and tighten scope. + +If uncertain, query web_search with: +`site:github.com/ffuf/ffuf README` diff --git a/strix/skills/tooling/httpx.md b/strix/skills/tooling/httpx.md new file mode 100644 index 0000000..50fcf53 --- /dev/null +++ b/strix/skills/tooling/httpx.md @@ -0,0 +1,77 @@ +--- +name: httpx +description: ProjectDiscovery httpx probing syntax, exact probe flags, and automation-safe output patterns. +--- + +# httpx CLI Playbook + +Official docs: +- https://docs.projectdiscovery.io/opensource/httpx/usage +- https://docs.projectdiscovery.io/opensource/httpx/running +- https://github.com/projectdiscovery/httpx + +Canonical syntax: +`httpx [flags]` + +High-signal flags: +- `-u, -target ` single target +- `-l, -list ` target list +- `-nf, -no-fallback` probe both HTTP and HTTPS +- `-nfs, -no-fallback-scheme` do not auto-switch schemes +- `-sc` status code +- `-title` page title +- `-server, -web-server` server header +- `-td, -tech-detect` technology detection +- `-fr, -follow-redirects` follow redirects +- `-mc ` / `-fc ` match or filter status codes +- `-path ` probe specific paths +- `-p, -ports ` probe custom ports +- `-proxy, -http-proxy ` proxy target requests +- `-tlsi, -tls-impersonate` experimental TLS impersonation +- `-j, -json` JSONL output +- `-sr, -store-response` store request/response artifacts +- `-srd, -store-response-dir ` custom directory for stored artifacts +- `-silent` compact output +- `-rl ` requests/second cap +- `-t ` threads +- `-timeout ` request timeout +- `-retries ` retry attempts +- `-o ` output file + +Agent-safe baseline for automation: +`httpx -l hosts.txt -sc -title -server -td -fr -timeout 10 -retries 1 -rl 50 -t 25 -silent -j -o httpx.jsonl` + +Common patterns: +- Quick live+fingerprint check: + `httpx -l hosts.txt -sc -title -server -td -silent -o httpx.txt` +- Probe known admin paths: + `httpx -l hosts.txt -path /,/login,/admin -sc -title -silent -j -o httpx_paths.jsonl` +- Probe both schemes explicitly: + `httpx -l hosts.txt -nf -sc -title -silent` +- Vhost detection pass: + `httpx -l hosts.txt -vhost -sc -title -silent -j -o httpx_vhost.jsonl` +- Proxy-instrumented probing: + `httpx -l hosts.txt -sc -title -proxy http://127.0.0.1:48080 -silent -j -o httpx_proxy.jsonl` +- Response-storage pass for downstream content parsing: + `httpx -l hosts.txt -fr -sr -srd recon/httpx_store -sc -title -server -cl -ct -location -probe -silent` + +Critical correctness rules: +- For machine parsing, prefer `-j -o `. +- Keep `-rl` and `-t` explicit for reproducible throughput. +- Use `-nf` when you need dual-scheme probing from host-only input. +- When using `-path` or `-ports`, keep scope tight to avoid accidental scan inflation. +- Use `-sr -srd ` when later steps need raw response artifacts (JS/route extraction, grepping, replay). + +Usage rules: +- Use `-silent` for pipeline-friendly output. +- Use `-mc/-fc` when downstream steps depend on specific response classes. +- Prefer `-proxy` flag over global proxy env vars when only httpx traffic should be proxied. +- Do not use `-h`/`--help` for routine runs unless absolutely necessary. + +Failure recovery: +- If too many timeouts occur, reduce `-rl/-t` and/or increase `-timeout`. +- If output is noisy, add `-fc` filters or `-fd` duplicate filtering. +- If HTTPS-only probing misses HTTP services, rerun with `-nf` (and avoid `-nfs`). + +If uncertain, query web_search with: +`site:docs.projectdiscovery.io httpx usage` diff --git a/strix/skills/tooling/katana.md b/strix/skills/tooling/katana.md new file mode 100644 index 0000000..258e8e0 --- /dev/null +++ b/strix/skills/tooling/katana.md @@ -0,0 +1,76 @@ +--- +name: katana +description: Katana crawler syntax, depth/js/known-files behavior, and stable concurrency controls. +--- + +# Katana CLI Playbook + +Official docs: +- https://docs.projectdiscovery.io/opensource/katana/usage +- https://docs.projectdiscovery.io/opensource/katana/running +- https://github.com/projectdiscovery/katana + +Canonical syntax: +`katana [flags]` + +High-signal flags: +- `-u, -list ` target URL(s) +- `-d, -depth ` crawl depth +- `-jc, -js-crawl` parse JavaScript-discovered endpoints +- `-jsl, -jsluice` deeper JS parsing (memory intensive) +- `-kf, -known-files ` known-file crawling mode +- `-proxy ` explicit proxy setting +- `-c, -concurrency ` concurrent fetchers +- `-p, -parallelism ` concurrent input targets +- `-rl, -rate-limit ` request rate limit +- `-timeout ` request timeout +- `-retry ` retry count +- `-ef, -extension-filter ` extension exclusions +- `-tlsi, -tls-impersonate` experimental JA3/TLS impersonation +- `-hl, -headless` enable hybrid headless crawling +- `-sc, -system-chrome` use local Chrome for headless mode +- `-ho, -headless-options ` extra Chrome options (for example proxy-server) +- `-nos, -no-sandbox` run Chrome headless with no-sandbox +- `-noi, -no-incognito` disable incognito in headless mode +- `-cdd, -chrome-data-dir ` persist browser profile/session +- `-xhr, -xhr-extraction` include XHR endpoints in JSONL output +- `-silent`, `-j, -jsonl`, `-o ` output controls + +Agent-safe baseline for automation: +`mkdir -p crawl && katana -u https://target.tld -d 3 -jc -kf robotstxt -c 10 -p 10 -rl 50 -timeout 10 -retry 1 -ef png,jpg,jpeg,gif,svg,css,woff,woff2,ttf,eot,map -silent -j -o crawl/katana.jsonl` + +Common patterns: +- Fast crawl baseline: + `katana -u https://target.tld -d 3 -jc -silent` +- Deeper JS-aware crawl: + `katana -u https://target.tld -d 5 -jc -jsl -kf all -c 10 -p 10 -rl 50 -o katana_urls.txt` +- Multi-target run with JSONL output: + `katana -list urls.txt -d 3 -jc -silent -j -o katana.jsonl` +- Headless crawl with local Chrome: + `katana -u https://target.tld -hl -sc -nos -xhr -j -o crawl/katana_headless.jsonl` +- Headless crawl through proxy: + `katana -u https://target.tld -hl -sc -ho proxy-server=http://127.0.0.1:48080 -j -o crawl/katana_proxy.jsonl` + +Critical correctness rules: +- `-kf` must be followed by one of `all`, `robotstxt`, or `sitemapxml`. +- Use documented `-hl` for headless mode. +- `-proxy` expects a single proxy URL string (for example `http://127.0.0.1:8080`). +- `-ho` expects comma-separated Chrome options (example: `-ho --disable-gpu,proxy-server=http://127.0.0.1:8080`). +- For `-kf`, keep depth at least `-d 3` so known files are fully covered. +- If writing to a file, ensure parent directory exists before `-o`. + +Usage rules: +- Keep `-d`, `-c`, `-p`, and `-rl` explicit for reproducible runs. +- Use `-ef` early to reduce static-file noise before fuzzing. +- Prefer `-proxy` over environment proxy variables when proxying only Katana traffic. +- Use `-hc` only for one-time diagnostics, not routine crawling loops. +- Do not use `-h`/`--help` for routine runs unless absolutely necessary. + +Failure recovery: +- If crawl runs too long, lower `-d` and optionally add `-ct`. +- If memory spikes, disable `-jsl` and lower `-c/-p`. +- If headless fails with Chrome errors, drop `-sc` or install system Chrome. +- If output is noisy, tighten scope and add `-ef` filters. + +If uncertain, query web_search with: +`site:docs.projectdiscovery.io katana usage` diff --git a/strix/skills/tooling/naabu.md b/strix/skills/tooling/naabu.md new file mode 100644 index 0000000..f39d44b --- /dev/null +++ b/strix/skills/tooling/naabu.md @@ -0,0 +1,68 @@ +--- +name: naabu +description: Naabu port-scanning syntax with host input, scan-type, verification, and rate controls. +--- + +# Naabu CLI Playbook + +Official docs: +- https://docs.projectdiscovery.io/opensource/naabu/usage +- https://docs.projectdiscovery.io/opensource/naabu/running +- https://github.com/projectdiscovery/naabu + +Canonical syntax: +`naabu [flags]` + +High-signal flags: +- `-host ` single host +- `-list, -l ` hosts list +- `-p ` explicit ports (supports ranges) +- `-top-ports ` top ports profile +- `-exclude-ports ` exclusions +- `-scan-type ` SYN or CONNECT scan +- `-Pn` skip host discovery +- `-rate ` packets per second +- `-c ` worker count +- `-timeout ` per-probe timeout in milliseconds +- `-retries ` retry attempts +- `-proxy ` SOCKS5 proxy +- `-verify` verify discovered open ports +- `-j, -json` JSONL output +- `-silent` compact output +- `-o ` output file + +Agent-safe baseline for automation: +`naabu -list hosts.txt -top-ports 100 -scan-type c -Pn -rate 300 -c 25 -timeout 1000 -retries 1 -verify -silent -j -o naabu.jsonl` + +Common patterns: +- Top ports with controlled rate: + `naabu -list hosts.txt -top-ports 100 -scan-type c -rate 300 -c 25 -timeout 1000 -retries 1 -verify -silent -o naabu.txt` +- Focused web-ports sweep: + `naabu -list hosts.txt -p 80,443,8080,8443 -scan-type c -rate 300 -c 25 -timeout 1000 -retries 1 -verify -silent` +- Single-host quick check: + `naabu -host target.tld -p 22,80,443 -scan-type c -rate 300 -c 25 -timeout 1000 -retries 1 -verify` +- Root SYN mode (if available): + `sudo naabu -list hosts.txt -top-ports 100 -scan-type syn -rate 500 -c 25 -timeout 1000 -retries 1 -verify -silent` + +Critical correctness rules: +- Use `-scan-type connect` when running without root/privileged raw socket access. +- Always set `-timeout` explicitly; it is in milliseconds. +- Set `-rate` explicitly to avoid unstable or noisy scans. +- `-timeout` is in milliseconds, not seconds. +- Keep port scope tight: prefer explicit important ports or a small `-top-ports` value unless broader coverage is explicitly required. +- Do not spam traffic; start with the smallest useful port set and conservative rate/worker settings. +- Prefer `-verify` before handing ports to follow-up scanners. + +Usage rules: +- Keep host discovery behavior explicit (`-Pn` or default discovery). +- Use `-j -o ` for automation pipelines. +- Prefer `-p 22,80,443,8080,8443` or `-top-ports 100` before considering larger sweeps. +- Do not use `-h`/`--help` for normal flow unless absolutely necessary. + +Failure recovery: +- If privileged socket errors occur, switch to `-scan-type c`. +- If scans are slow or lossy, lower `-rate`, lower `-c`, and tighten `-p`/`-top-ports`. +- If many hosts appear down, compare runs with and without `-Pn`. + +If uncertain, query web_search with: +`site:docs.projectdiscovery.io naabu usage` diff --git a/strix/skills/tooling/nmap.md b/strix/skills/tooling/nmap.md new file mode 100644 index 0000000..831b4c6 --- /dev/null +++ b/strix/skills/tooling/nmap.md @@ -0,0 +1,66 @@ +--- +name: nmap +description: Canonical Nmap CLI syntax, two-pass scanning workflow, and sandbox-safe bounded scan patterns. +--- + +# Nmap CLI Playbook + +Official docs: +- https://nmap.org/book/man-briefoptions.html +- https://nmap.org/book/man.html +- https://nmap.org/book/man-performance.html + +Canonical syntax: +`nmap [Scan Type(s)] [Options] {target specification}` + +High-signal flags: +- `-n` skip DNS resolution +- `-Pn` skip host discovery when ICMP/ping is filtered +- `-sS` SYN scan (root/privileged) +- `-sT` TCP connect scan (no raw-socket privilege) +- `-sV` detect service versions +- `-sC` run default NSE scripts +- `-p ` explicit ports (`-p-` for all TCP ports) +- `--top-ports ` quick common-port sweep +- `--open` show only hosts with open ports +- `-T<0-5>` timing template (`-T4` common) +- `--max-retries ` cap retransmissions +- `--host-timeout