Conversation
|
| @@ -0,0 +1,57 @@ | |||
| # Strix 2 — Authorized Scope Policy (TEMPLATE) | |||
There was a problem hiding this comment.
A scan started from the repository root loads the checked-in scope.yaml automatically. Even if the operator did not choose a scope file, this example policy rejects targets outside its sample entries. Make the template opt-in rather than active by default.
Prompt To Fix With AI
This is a comment left during a code review.
Path: scope.yaml
Line: 1
Comment:
**Example scope blocks scans**
A scan started from the repository root loads the checked-in `scope.yaml` automatically. Even if the operator did not choose a scope file, this example policy rejects targets outside its sample entries. Make the template opt-in rather than active by default.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| ) | ||
|
|
||
|
|
||
| def enforce_shell_command(command: str) -> ScopeDecision | None: |
There was a problem hiding this comment.
enforce_shell_command is never called by the shell tool. With a policy loaded, an agent can run nmap 8.8.8.8 or curl https://outside.example through exec_command without a scope check. Wire the check into the shell boundary before dispatch.
How this was verified: The shell wrapper dispatches commands without calling enforce_shell_command.
Knowledge Base Used: Agent tools and workflows
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/scope/enforcement.py
Line: 165
Comment:
**Shell commands escape scope**
`enforce_shell_command` is never called by the shell tool. With a policy loaded, an agent can run `nmap 8.8.8.8` or `curl https://outside.example` through `exec_command` without a scope check. Wire the check into the shell boundary before dispatch.
**How this was verified:** The shell wrapper dispatches commands without calling `enforce_shell_command`.
**Knowledge Base Used:** [Agent tools and workflows](https://app.greptile.com/strix-org-3/-/custom-context/knowledge-base/usestrix/strix/-/docs/agent-tools-and-workflows.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| return invalid_arguments | ||
| if arguments is not None and not isinstance(arguments, dict): | ||
| return invalid_arguments | ||
| denial = _scope_denial(arguments) |
There was a problem hiding this comment.
The new policy check covers call_mcp, but not repeat_request. An agent can set modifications["url"] to an unauthorized host, and the proxy sends the request without checking the loaded policy. Check the final replay URL before sending.
How this was verified: repeat_request builds and sends the modified URL without calling a scope guard.
Knowledge Base Used: Agent tools and workflows
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/tools/mcp/agent_tools.py
Line: 294
Comment:
**Proxy replay escapes scope**
The new policy check covers `call_mcp`, but not `repeat_request`. An agent can set `modifications["url"]` to an unauthorized host, and the proxy sends the request without checking the loaded policy. Check the final replay URL before sending.
**How this was verified:** `repeat_request` builds and sends the modified URL without calling a scope guard.
**Knowledge Base Used:** [Agent tools and workflows](https://app.greptile.com/strix-org-3/-/custom-context/knowledge-base/usestrix/strix/-/docs/agent-tools-and-workflows.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| # High-confidence target patterns only, so a scope check never rejects a legit | ||
| # MCP call over an ambiguous string. Bare hostnames are intentionally excluded | ||
| # here (too many false positives); domain tools check those against their known | ||
| # target argument directly. | ||
| _URL_RE = re.compile(r"\b[a-z][a-z0-9+.\-]*://[^\s\"'<>]+", re.IGNORECASE) | ||
| _ARN_RE = re.compile(r"\barn:aws:[^\s\"'<>]+", re.IGNORECASE) | ||
| _ACCOUNT_RE = re.compile(r"\b\d{12}\b") | ||
| _IPV4_RE = re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b") |
There was a problem hiding this comment.
extract_targets skips bare hostnames. An MCP call such as {"host": "outside.example"} therefore passes _scope_denial without a decision. No domain wrapper in this PR checks the argument later. Check known target fields before dispatch instead of treating an empty extraction as approval.
How this was verified: The extractor ignores bare hostnames, and call_mcp dispatches when it returns no denial.
Knowledge Base Used: MCP integration
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/scope/enforcement.py
Line: 37-44
Comment:
**Hostnames bypass MCP scope**
`extract_targets` skips bare hostnames. An MCP call such as `{"host": "outside.example"}` therefore passes `_scope_denial` without a decision. No domain wrapper in this PR checks the argument later. Check known target fields before dispatch instead of treating an empty extraction as approval.
**How this was verified:** The extractor ignores bare hostnames, and `call_mcp` dispatches when it returns no denial.
**Knowledge Base Used:** [MCP integration](https://app.greptile.com/strix-org-3/-/custom-context/knowledge-base/usestrix/strix/-/docs/mcp-integration.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| normalized_target = norm(target) | ||
| normalized_base = norm(base) | ||
| if not normalized_target.startswith(normalized_base): | ||
| return False | ||
| remainder = normalized_target[len(normalized_base) :] | ||
| return remainder == "" or remainder[0] in "/?#" |
There was a problem hiding this comment.
_url_prefix_matches compares the raw URL without resolving .. path segments. With only https://api.example.com/v1 authorized, it accepts https://api.example.com/v1/../admin because the rest starts with /, even though the request can reach /admin. Compare the resolved request path with the authorized base.
How this was verified: The matcher accepts the raw /../admin suffix, and the MCP boundary forwards the original arguments.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/scope/schema.py
Line: 320-325
Comment:
**API path check can be escaped**
`_url_prefix_matches` compares the raw URL without resolving `..` path segments. With only `https://api.example.com/v1` authorized, it accepts `https://api.example.com/v1/../admin` because the rest starts with `/`, even though the request can reach `/admin`. Compare the resolved request path with the authorized base.
**How this was verified:** The matcher accepts the raw `/../admin` suffix, and the MCP boundary forwards the original arguments.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| for base in self.api.base_urls: | ||
| if _url_prefix_matches(target, base): | ||
| reason = f"matches api.base_urls entry {base!r}" | ||
| return ScopeDecision.allow(reason, "api", "api", base) |
There was a problem hiding this comment.
The API-base branch returns approval before checking network.ports. If an operator authorizes https://api.example.com:8443/v1 as an API base but allows only port 443, a call to port 8443 passes. Apply the configured port limit before approving this branch.
How this was verified: An API prefix match returns immediately; only the later branches check the port.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/scope/schema.py
Line: 220-223
Comment:
**API calls skip port limits**
The API-base branch returns approval before checking `network.ports`. If an operator authorizes `https://api.example.com:8443/v1` as an API base but allows only port 443, a call to port 8443 passes. Apply the configured port limit before approving this branch.
**How this was verified:** An API prefix match returns immediately; only the later branches check the port.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| for domain in self.web.domains: | ||
| if _host_matches(host, domain): | ||
| port_ok, port_reason = self._port_ok(parsed.port) |
There was a problem hiding this comment.
_check_url reads parsed.port without catching ValueError. If an agent checks http://example.com:abc, it gets a tool error instead of a clear scope denial. Return a denied ScopeDecision for an invalid port.
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/scope/schema.py
Line: 225-227
Comment:
**Bad ports break scope checks**
`_check_url` reads `parsed.port` without catching `ValueError`. If an agent checks `http://example.com:abc`, it gets a tool error instead of a clear scope denial. Return a denied `ScopeDecision` for an invalid port.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| teardown_logging = setup_scan_logging(run_dir) | ||
| set_scan_id(scan_id) | ||
| # Strix 2: register additive agent tools + load the scope policy at run start. | ||
| install_strix2_extensions(run_dir) |
There was a problem hiding this comment.
Failed starts keep log handlers
install_strix2_extensions can raise for a malformed scope file after scan logging starts but before the runner's cleanup block begins. The run leaves its log handlers attached and its file open. Load the policy before opening scan resources, or cover this call with cleanup.
Knowledge Base Used: Scan execution lifecycle
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/core/runner.py
Line: 231
Comment:
**Failed starts keep log handlers**
`install_strix2_extensions` can raise for a malformed scope file after scan logging starts but before the runner's cleanup block begins. The run leaves its log handlers attached and its file open. Load the policy before opening scan resources, or cover this call with cleanup.
**Knowledge Base Used:** [Scan execution lifecycle](https://app.greptile.com/strix-org-3/-/custom-context/knowledge-base/usestrix/strix/-/docs/scan-execution.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| - name: Install uv | ||
| uses: astral-sh/setup-uv@v5 |
There was a problem hiding this comment.
CI actions can change underfoot
The new CI workflow uses mutable action tags, while the existing release workflow pins those actions to commit hashes. A moved tag could change what code CI runs without a repository change. Pin checkout and setup actions here too.
Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/ci.yml
Line: 36-37
Comment:
**CI actions can change underfoot**
The new CI workflow uses mutable action tags, while the existing release workflow pins those actions to commit hashes. A moved tag could change what code CI runs without a repository change. Pin checkout and setup actions here too.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| ## Landed — Phase 1 wrappers | ||
|
|
||
| | Tool | Domain | How wrapped | License | Conf. | | ||
| |---|---|---|---|---| | ||
| | boto3 (AWS SDK) | cloud API calls (read-only validation PoC) | host-side MCP wrapper `strix/mcp_servers/aws.py`, **imported as a library** (not a subprocess) | Apache-2.0 | ✅ | |
There was a problem hiding this comment.
Cloud wrapper listed too early
THIRD_PARTY.md lists strix/mcp_servers/aws.py as landed and scope-gated, but this PR contains no strix/mcp_servers package. Mark the wrapper as planned until it lands so readers do not rely on a cloud guard that is not present.
Prompt To Fix With AI
This is a comment left during a code review.
Path: THIRD_PARTY.md
Line: 61-65
Comment:
**Cloud wrapper listed too early**
`THIRD_PARTY.md` lists `strix/mcp_servers/aws.py` as landed and scope-gated, but this PR contains no `strix/mcp_servers` package. Mark the wrapper as planned until it lands so readers do not rely on a cloud guard that is not present.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Adds the authorized-scope policy and enforcement that the other Strix 2 areas build on, all additive and opt-in (no-op without a scope.yaml): - strix/scope/: ScopePolicy schema (web/api/network/cloud) + fail-closed loader + runtime enforcement (enforce_target/arguments/shell_command). - strix/tools/scope/: check_scope / scope_status agent tools. - scope check wired at the call_mcp boundary (tools/mcp/agent_tools.py). - --scope-config / --allow-intrusive CLI flags. - strix/strix2_ext.py: the single idempotent install hook (registers Strix 2 tools + loads the policy), called once from run_strix_scan (the one core edit). - CI (lint + mypy + Strix 2 tests + baseline), design docs, THIRD_PARTY, a scope.yaml template (no live targets). First of a dependency-ordered series splitting usestrix#1394; the candidate tier, cloud wrapper, and efficiency/eval areas build on this. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
a75aa77 to
56caebf
Compare
1 of 4 — splits #1394 into per-area, dependency-ordered PRs. This is the base the others build on.
Adds the authorized-scope policy and enforcement, all additive and opt-in (a run with no
scope.yamlis unchanged):strix/scope/—ScopePolicyschema (web / api / network / cloud) + a fail-closed loader + runtime enforcement (enforce_target/enforce_arguments/enforce_shell_command).strix/tools/scope/—check_scope/scope_statusagent tools.call_mcpboundary (tools/mcp/agent_tools.py), no-op without a policy.--scope-config/--allow-intrusiveCLI flags.strix/strix2_ext.py— the single idempotent install hook (registers Strix 2 tools + loads the policy), called once fromrun_strix_scan(the one core edit).docs/strix2/,THIRD_PARTY.md, and ascope.yamltemplate with no live targets.Review / merge order
This series is dependency-ordered and meant to merge in order:
Until this merges, the later PRs' diffs include these foundation commits; they shrink automatically as this lands.
CI (lint + mypy + Strix 2 tests) is green on Python 3.12 and 3.13.
🤖 Generated with Claude Code