SkillTotal

Is usestrix/strix safe?

No malicious indicators - review capabilities before installing
Notable — review in context (capabilities are not malware):
  • Python shell/command execution
  • Python network egress
  • Python filesystem read

What to do: Nothing here argues against installing it. Grant the capabilities it lists only if you expect the tool to need them.

usestrix/strix is a PyPI package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 1 risky construct is reported for review. It can: delegated authentication, filesystem read, filesystem write, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 10/100 (low).

strix-agent 1.7.0

python_package · https://github.com/usestrix/strix
LOW
10
/ 100 risk score
Snapshot · scanned Oct 8, 2026 · strix-agent@1.7.0 · engine 0.56.4 / ruleset 62

Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of usestrix/strix's authors. Report a false positive.

Capabilities — what this component can do (not a risk score):
delegated authenticationfilesystem readfilesystem writenetwork egressshell execution

Behavioral traits

How this component maps to the CSA agentic threat model. Descriptive — it never affects the risk score.

Execution authority
Tool Access Control / Direct Tool Access
Filesystem reach
Tool Execution Context
Network egress
Interaction & Communication / Direct Communication
Network exposure
Interaction & Communication
Delegated authentication
Tool Execution Context / User Delegated Credentials

Findings (7)

HIGHPython shell/command executionST-SHELL-PY

The component can run operating-system commands or spawn processes.

subprocess.run(  # noqa: S603 - fixed build command using the resolved Go binary
            [
                go,
                "build",
                "-trimpath",
                "-ldflags=-s -w",
                "-o", …
process = subprocess.run(  # noqa: S603
                        command,
                        check=False,
                        capture_output=capture_output,
                        text=True,
                        env=wallet_env, …
return subprocess.run(  # noqa: S603
                    command,
                    check=False,
                    capture_output=True,
                    text=True,
                    env=wallet_env,
                    cwd=wallet_ro …
return subprocess.run(  # noqa: S603
            [*_npx_prefix(npx, wallet_root), _LINK_CLI_PACKAGE, *arguments],
            check=False,
            capture_output=capture_output,
            text=True,
            env=_wallet_environment …
process = subprocess.Popen(  # noqa: S603  # nosec B603
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
        )
result = subprocess.run(  # noqa: S603  # nosec B603
        [git, "-C", str(source), "rev-parse", "--show-toplevel"],
        check=False,
        capture_output=True,
        text=True,
    )
process = await asyncio.create_subprocess_exec(
            *command, env=env, cwd=cwd, pass_fds=(child_socket.fileno(),)
        )
windows_process = subprocess.Popen(command, env=env, cwd=cwd)  # noqa: S603
result = subprocess.run(command, check=False)  # noqa: S603
return subprocess.run(  # noqa: S603
        ["git", "-C", str(repo_path), *args],  # noqa: S607
        capture_output=True,
        text=True,
        check=check,
    )
return subprocess.run(  # noqa: S603
        ["git", "-C", str(repo_path), *args],  # noqa: S607
        capture_output=True,
        check=check,
    )
subprocess.run(  # noqa: S603
                [
                    git_executable,
                    "clone",
                    repo_url,
                    str(clone_path),
                ],
                capture_output=True, …
result = subprocess.run(  # noqa: S603
                ["git", "-C", str(path), *args],  # noqa: S607
                capture_output=True,
                text=True,
                check=False,
                timeout=5,
            )
result = subprocess.run(  # noqa: S603
            ["git", "-C", str(repo), *args],  # noqa: S607
            capture_output=True,
            text=True,
            check=False,
            timeout=_GIT_TIMEOUT_SECONDS,
        )

Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.

Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; avoid shell=True.

MEDIUMServer bound to all network interfacesST-EXPOSE-BIND

A server is bound to all network interfaces (0.0.0.0), not just your own machine.

if host_lower in ("localhost", "0.0.0.0", "::1"):  # nosec B104

Why it matters: Without authentication, other hosts on the network can reach it.

Fix: Bind to 127.0.0.1 for local-only use, or require authentication and restrict access if remote exposure is intended.

MEDIUMPython filesystem readST-FS-PY-READ

The component reads files from disk.

data = json.loads(AUTH_PATH.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8"))
snap = json.loads(agents_path.read_text(encoding="utf-8"))
data = _LOGO_PATH.read_bytes()
text = path.read_text(encoding="utf-8")
data = json.loads(AUTH_PATH.read_text(encoding="utf-8"))
raw = json.loads(IDENTITY_PATH.read_text(encoding="utf-8"))
agents_data = json.loads(agents_path.read_text(encoding="utf-8"))
record = json.loads((run_dir / "run.json").read_text(encoding="utf-8"))
return Path(os.devnull).open("a", buffering=1, encoding="utf-8")
content = git_meta.read_text(encoding="utf-8").strip()
payload = json.loads(path.read_text(encoding="utf-8"))
for line in path.read_text(encoding="utf-8").splitlines()
"content": source.read_bytes(),
data = json.loads(AUTH_PATH.read_text(encoding="utf-8"))
return report_path.read_text(encoding="utf-8")
return json.loads(path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8"))
return json.loads(path.read_text(encoding="utf-8"))
data = json.loads(json_path.read_text(encoding="utf-8"))
data = json.loads(path.read_text(encoding="utf-8"))
content = git_file.read_text(encoding="utf-8", errors="replace")
content = file_path.read_text(encoding="utf-8")
content = file_path.read_text(encoding="utf-8")

Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.

Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.

MEDIUMPython filesystem write/deleteST-FS-PY-WRITE

The component writes or deletes files on disk.

_CACHE_PATH.write_text(json.dumps(cache), encoding="utf-8")
shutil.copy2(new_binary, staged)
path.write_text(json.dumps(collection, indent=2), encoding="utf-8")
shutil.copy2(source, staging / name)
atomic_write_text(path, json.dumps(document, ensure_ascii=False, indent=2, default=str))
atomic_write_text(
        run_record_path(run_dir),
        json.dumps(run_record, ensure_ascii=False, indent=2, default=str),
    )
atomic_write_text(
            vuln_dir / f"{report['id']}.md",
            render_vulnerability_md(report),
        )
atomic_write_text(csv_path, csv_buf.getvalue())
atomic_write_text(
        run_dir / "vulnerabilities.json",
        json.dumps(vulnerability_reports, ensure_ascii=False, indent=2, default=str),
    )

Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.

Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.

MEDIUMNode.js network egressST-NET-NODE

The component makes outbound network requests.

const res = await fetch(path, { cache: "no-store" });
void fetch("/api/event", { method: "POST", body: payload, keepalive: true });

Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.

Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.

MEDIUMPython network egressST-NET-PY

The component makes outbound network requests.

with requests.post(
            TOKEN_URL,
            data=payload,
            headers={"Accept": "application/json"},
            timeout=_TOKEN_TIMEOUT,
        ) as response:
http_client = httpx.AsyncClient(
        timeout=httpx.Timeout(600.0, connect=30.0),
        event_hooks={"request": [_auth_hook]},
    )
timeout=httpx.Timeout(600.0, connect=30.0),
response = requests.request(
            method,
            url,
            headers=headers,
            params={
                key: ("true" if value else "false") if isinstance(value, bool) else value
                for key, value in …
response = requests.put(
                signed_url,
                data=stream,
                headers={
                    "Authorization": f"Bearer {upload_token}",
                    "Content-Type": "application/zip", …
response = requests.request(
                    "POST",
                    state.upstream_url,
                    headers=headers,
                    data=body,
                    timeout=state.timeout,
                    allow_redire …
response = requests.post(
            f"{app_url}/api/v1/cli/login",
            timeout=_HTTP_TIMEOUT_S,
            allow_redirects=False,
        )
poll = requests.post(
                f"{app_url}/api/v1/cli/login/poll",
                json=poll_body,
                timeout=_HTTP_TIMEOUT_S,
                allow_redirects=False,
            )
response = requests.post(
            f"{app_url}/api/v1/cli/login/complete",
            json=body,
            timeout=_HTTP_TIMEOUT_S,
            allow_redirects=False,
        )
response = requests.delete(
            f"{app_url.rstrip('/')}/api/v1/cli/session",
            headers=_session_headers(record),
            timeout=_HTTP_TIMEOUT_S,
            allow_redirects=False,
        )
requests.delete(
            f"{previous['app_url']}/api/v1/cli/session",
            headers=_session_headers(previous),
            timeout=_HTTP_TIMEOUT_S,
            allow_redirects=False,
        )
with requests.get(
                f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest",
                timeout=REQUEST_TIMEOUT_SECONDS,
            ) as response:
with requests.get(
            f"https://pypi.org/pypi/{PYPI_PACKAGE}/json",
            timeout=REQUEST_TIMEOUT_SECONDS,
        ) as response:
with requests.get(
            f"https://api.github.com/repos/{GITHUB_REPO}/releases/tags/v{version}",
            timeout=REQUEST_TIMEOUT_SECONDS,
        ) as response:
with requests.get(  # nosec B113
            url,
            stream=True,
            timeout=REQUEST_TIMEOUT_SECONDS * 12,
        ) as response:
with requests.get(check_url, headers={"User-Agent": "git/2.43.0"}, timeout=10) as resp:

Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.

Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.

LOWDelegated authentication (OAuth 2.0 / OIDC)ST-AUTH-DELEGATED

An OAuth 2.0 / OpenID Connect delegated-authentication flow was detected (authorization-code / refresh-token / token-exchange grant, an OIDC authorize/discovery endpoint or id_token, or a delegation library). Tools authenticate with the end user's delegated, scoped credentials rather than a long-lived embedded service credential. (5 occurrence(s) shown as evidence).

AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize"
data.get("id_token") if isinstance(data.get("id_token"), str) else ""
"grant_type": "authorization_code",
"grant_type": "refresh_token",

Fix: Delegated auth is a lower-blast-radius execution context than an embedded static credential. Confirm the requested scopes are minimal and that tokens are never logged or forwarded off-host.

How attackers abuse these capabilities

Interactive labs on the attack class behind the rules above. They show the technique, not anything found in usestrix/strix.

Check your own component

Run the same evidence-backed scan on any MCP server, agent skill, or package.

Scan your own component

How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →