From c21c4d26b2023d9d9b0f0b03c500766413df6b94 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 21 Mar 2026 10:16:11 +0100 Subject: [PATCH] fix code-review issues and add 8 new vulnerability skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checkpoint / resume fixes (bot review on PR #380): - cli.py: skip checkpoint save when scan completed cleanly (agent.state.completed) to prevent stale checkpoint re-creating after base_agent.py deletes it - tui.py: same completed guard in both _save_checkpoint_on_interrupt and action_custom_quit to cover all TUI exit paths - checkpoint_restore.py: fix infinite recursion in _depth() for cyclic parent_id references in corrupted checkpoints — mark node before recursing - config.py: restore original shell-env-wins precedence for LLM vars; cli-config.json only applies when the shell var is absent, preventing silent override of rotated keys managed via shell environment New vulnerability skills (from upstream PRs #204 and #334): - clickjacking, cors_misconfiguration, nosql_injection, prototype_pollution, ssti, websocket_security (PR #204) - mfa_bypass, edge_cases (PR #334) Co-Authored-By: Claude Sonnet 4.6 --- strix/config/config.py | 5 +- strix/interface/checkpoint_restore.py | 9 +- strix/interface/cli.py | 6 +- strix/interface/tui.py | 21 ++-- strix/skills/vulnerabilities/clickjacking.md | 71 ++++++++++++++ .../vulnerabilities/cors_misconfiguration.md | 73 ++++++++++++++ strix/skills/vulnerabilities/edge_cases.md | 95 +++++++++++++++++++ strix/skills/vulnerabilities/mfa_bypass.md | 90 ++++++++++++++++++ .../skills/vulnerabilities/nosql_injection.md | 78 +++++++++++++++ .../vulnerabilities/prototype_pollution.md | 75 +++++++++++++++ strix/skills/vulnerabilities/ssti.md | 86 +++++++++++++++++ .../vulnerabilities/websocket_security.md | 84 ++++++++++++++++ 12 files changed, 676 insertions(+), 17 deletions(-) create mode 100644 strix/skills/vulnerabilities/clickjacking.md create mode 100644 strix/skills/vulnerabilities/cors_misconfiguration.md create mode 100644 strix/skills/vulnerabilities/edge_cases.md create mode 100644 strix/skills/vulnerabilities/mfa_bypass.md create mode 100644 strix/skills/vulnerabilities/nosql_injection.md create mode 100644 strix/skills/vulnerabilities/prototype_pollution.md create mode 100644 strix/skills/vulnerabilities/ssti.md create mode 100644 strix/skills/vulnerabilities/websocket_security.md diff --git a/strix/config/config.py b/strix/config/config.py index 4cf6e600..6f98d920 100644 --- a/strix/config/config.py +++ b/strix/config/config.py @@ -141,11 +141,10 @@ class Config: cls.save({"env": env_vars}) applied = {} - llm_vars = cls._llm_env_vars() for var_name, var_value in env_vars.items(): if var_name in cls.tracked_vars(): - # LLM vars in cli-config.json always win over shell env - if var_name in llm_vars or force or var_name not in os.environ: + # Shell env wins unless --force or the var is not set in shell. + if force or var_name not in os.environ: os.environ[var_name] = var_value applied[var_name] = var_value diff --git a/strix/interface/checkpoint_restore.py b/strix/interface/checkpoint_restore.py index 5a9f1dc0..b4a2f830 100644 --- a/strix/interface/checkpoint_restore.py +++ b/strix/interface/checkpoint_restore.py @@ -34,12 +34,11 @@ def restore_sub_agents(checkpoint_data: Any, llm_config: Any) -> list[str]: def _depth(aid: str) -> int: if aid in _memo: return _memo[aid] + # Mark before recursing to break any cycle in corrupted checkpoints. + _memo[aid] = 0 parent = sub_agent_states.get(aid, {}).get("parent_id") - _memo[aid] = ( - 0 - if (parent is None or parent not in sub_agent_states) - else 1 + _depth(parent) - ) + if parent is not None and parent in sub_agent_states: + _memo[aid] = 1 + _depth(parent) return _memo[aid] restored_ids: list[str] = [] diff --git a/strix/interface/cli.py b/strix/interface/cli.py index 3950e693..7bf88f91 100644 --- a/strix/interface/cli.py +++ b/strix/interface/cli.py @@ -266,9 +266,13 @@ async def run_cli(args: Any) -> None: # noqa: PLR0915 return if _checkpoint_saved.is_set(): return + agent_instance = _agent_ref[0] + # Skip if the scan already completed successfully — the checkpoint + # was deleted in base_agent.py and there is nothing to resume. + if getattr(agent_instance.state, "completed", False): + return _checkpoint_saved.set() try: - agent_instance = _agent_ref[0] checkpoint_manager.save( agent_instance.state, tracer, diff --git a/strix/interface/tui.py b/strix/interface/tui.py index 09b048f8..c30cca7f 100644 --- a/strix/interface/tui.py +++ b/strix/interface/tui.py @@ -814,6 +814,9 @@ class StrixTUIApp(App): # type: ignore[misc] return if _checkpoint_saved.is_set(): return + # Skip if the scan already completed — nothing to resume. + if getattr(agent.state, "completed", False): + return _checkpoint_saved.set() try: mgr.save( @@ -2021,14 +2024,16 @@ class StrixTUIApp(App): # type: ignore[misc] _agent = getattr(self, "_current_agent", None) if _mgr and _agent: import contextlib - with contextlib.suppress(Exception): - _mgr.save( - _agent.state, - self.tracer, - self.scan_config, - self.agent_config.get("target_hash", ""), - _agent.max_iterations, - ) + # Only save if the scan was interrupted, not if it finished cleanly. + if not getattr(_agent.state, "completed", False): + with contextlib.suppress(Exception): + _mgr.save( + _agent.state, + self.tracer, + self.scan_config, + self.agent_config.get("target_hash", ""), + _agent.max_iterations, + ) self.tracer.cleanup() diff --git a/strix/skills/vulnerabilities/clickjacking.md b/strix/skills/vulnerabilities/clickjacking.md new file mode 100644 index 00000000..18b4219c --- /dev/null +++ b/strix/skills/vulnerabilities/clickjacking.md @@ -0,0 +1,71 @@ +--- +name: clickjacking +description: Clickjacking testing covering UI redressing, frame embedding, and X-Frame-Options / CSP bypass techniques +--- + +# Clickjacking + +Clickjacking (UI redressing) tricks users into clicking hidden or disguised UI elements by overlaying transparent iframes on top of legitimate pages. + +## Attack Surface + +**Targets** +- Pages that perform sensitive actions (fund transfers, account changes, password resets, OAuth authorization, social actions) +- Pages missing `X-Frame-Options` or `Content-Security-Policy: frame-ancestors` + +**Defenses to Bypass** +- `X-Frame-Options: DENY / SAMEORIGIN` +- `Content-Security-Policy: frame-ancestors 'none' / 'self'` +- Frame-busting JavaScript + +## Testing Methodology + +### Step 1 – Check Headers +``` +curl -s -I https://target.com | grep -i "x-frame-options\|frame-ancestors" +``` +Missing or misconfigured headers indicate framing is allowed. + +### Step 2 – Attempt Embedding +```html + +``` +If the page renders inside the iframe, the site is vulnerable. + +### Step 3 – Frame-Buster Bypass +If JavaScript frame-busting is used (e.g., `if (top !== self) top.location = self.location`): +- Use `sandbox` attribute to disable JS: `