Back to skill

Security audit

InStreet gomoku AI

Security checks for vulnerabilities and agentic risk

Overview

The skill is a disclosed InStreet Gomoku bot, but it ships a shared API key and can continuously create, join, and play games on a remote service.

Install only if you are comfortable with a bot that contacts the InStreet API, uses an embedded shared credential unless overridden, and can create or join rooms and submit moves automatically. The credential should be removed or rotated, users should provide their own token, and automatic game actions should require clearer user control.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
gomoku_bot.py:25
Finding

Published Shared API Credential Enables Unauthorized InStreet API Use

Content
View full analysis

Vulnerability Details

File Location: gomoku_bot.py:25, instreet_gomoku.py:11, and SKILL.md:103
Vulnerability Type: Hardcoded authentication credential
Risk Level: High

Vulnerable Code

gomoku_bot.py:25:

python
API_KEY = os.environ.get('INSTREET_API_KEY', 'sk_inst_adfe55c5fe69ca780201cb466bebbbce')

Representative transmission in gomoku_bot.py:38-43:

python
def get_activity():
    """获取当前活动状态"""
    url = f'{BASE_URL}/activity'
    req = urllib.request.Request(url, headers={'Authorization': f'Bearer {API_KEY}'})
    try:
        response = urllib.request.urlopen(req, timeout=10)

instreet_gomoku.py:11-12:

python
API_KEY = 'sk_inst_adfe55c5fe69ca780201cb466bebbbce'
BASE_URL = 'https://instreet.coze.site/api/v1/games'

Representative transmission in instreet_gomoku.py:855-861:

python
def get_activity():
    """获取当前活动"""
    url = f'{BASE_URL}/activity'
    req = urllib.request.Request(url, headers={'Authorization': f'Bearer {API_KEY}'})
    try:
        response = urllib.request.urlopen(req, timeout=10)
        data = json.loads(response.read().decode('utf-8'))

SKILL.md:103 also publishes the credential:

markdown
4. **API Key**:已配置在代码中 `sk_inst_adfe55c5fe69ca780201cb466bebbbce`

Technical Analysis

A live-looking bearer credential is embedded in two executable source files and explicitly disclosed in the published Skill documentation. Although gomoku_bot.py allows the environment to override it, the hardcoded token remains the default and is automatically placed in the Authorization header of outbound HTTPS requests.

The network destination is the declared InStreet game API, and transmitting an authorization token to that API is necessary for authenticated gameplay. Therefore, the transmission itself does not appear to be covert exfiltration. The vulnerability is that all Skill recipients receive and can reus ...[truncated 1729 chars]

Remediation
View remediation

Remediation Suggestions

  1. Immediately revoke and rotate the published credential.
  2. Remove the credential from all source files, documentation, package metadata, repository history, and published artifacts.
  3. Require callers to provide INSTREET_API_KEY; do not retain a default:
    python
    API_KEY = os.environ.get("INSTREET_API_KEY")
    if not API_KEY:
        raise RuntimeError("INSTREET_API_KEY must be configured")
    
  4. Issue separate credentials for individual users rather than distributing a shared account token.
  5. Restrict each credential to the minimum API scopes required to list, create, join, and play Gomoku games.
  6. Add server-side expiration, rotation, rate limiting, and anomalous-use monitoring.
  7. Add automated secret scanning to source-control and publication workflows.
  8. Avoid logging authorization headers or returning credentials in error messages.

T08 · Insecure Dependencies

Warning
Location
README.md:31
Finding

Unpinned and Unverified Package Installation Creates a Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: README.md:31-35
Vulnerability Type: Unpinned third-party dependency installation
Risk Level: Medium

Vulnerable Code

markdown
## 安装

```bash
pip install instreet-gomoku
text

### Technical Analysis

The installation instructions tell users to install a package by name from pip's configured package index without specifying an audited version, package hash, or verified source repository. Consequently, the package installed when a user follows this instruction may differ from the files reviewed in this audit.

Risks include:

- A future malicious or compromised package release.
- Package-owner account compromise or ownership transfer.
- Use of an attacker-controlled Python package index.
- Dependency confusion in environments with multiple indexes.
- Undetected changes to transitive dependencies.

The documentation also describes a `get_best_move(board, player=1, depth=4)` interface that does not match the included `get_best_move(board_str, my_color)` implementation. This mismatch increases uncertainty about whether the named package corresponds to the audited artifact.

No evidence shows that the current package is malicious. The confirmed weakness is that the installation process does not ensure users receive the reviewed version.

### Attack Path

1. An attacker compromises the package publisher, acquires control of the package name, or causes the victim to use a malicious package index.
2. The attacker publishes a modified package under the expected name or a higher-precedence source.
3. A user follows the documented `pip install instreet-gomoku` command.
4. pip resolves and downloads the attacker-controlled package instead of a cryptographically identified audited build.
5. Malicious package code executes during installation, import, or normal Skill operation with the privileges of the user running pip or Python.

### Impact Assessment

A su
...[truncated 434 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin the package to an explicitly audited version:
    bash
    python -m pip install instreet-gomoku==AUDITED_VERSION
    
  2. Publish and verify cryptographic hashes through a locked requirements file:
    bash
    python -m pip install --require-hashes -r requirements.txt
    
  3. Link to the official, verifiable source repository and package-publisher identity.
  4. Build reproducible release artifacts and sign release metadata where supported.
  5. Pin and review transitive dependencies rather than allowing unrestricted resolution.
  6. Avoid combining untrusted extra indexes with public indexes.
  7. Update the README API example so it accurately matches the audited implementation.

T09 · Insecure Skill Coding Practices

Warning
Location
katagomo_simple.py:115
Finding

KataGomo Engine Processes Remain Running After Timeouts

Content
View full analysis

Vulnerability Details

File Location: katagomo_simple.py:115-123 and katagomo_simple.py:162-164
Vulnerability Type: Improper subprocess timeout cleanup
Risk Level: Medium

Vulnerable Code

Subprocess creation and timed wait:

python
with open(cmd_file, 'r') as fin:
    proc = subprocess.Popen(
        [engine, "gtp", "-config", config, "-model", model, "-override-config", "basicRule=FREESTYLE"],
        stdin=fin,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        cwd=KATAGOMO_DIR,
        creationflags=subprocess.CREATE_NO_WINDOW if os.name == 'nt' else 0
    )
    output, _ = proc.communicate(timeout=120)

Timeout handler:

python
except subprocess.TimeoutExpired:
    print("[KataGomo] Timeout!")
    return None, None, None, "超时"

The timeout handler returns without terminating and reaping proc.

Technical Analysis

subprocess.Popen.communicate(timeout=120) raises subprocess.TimeoutExpired if the engine does not finish within 120 seconds. Python does not automatically kill the child process in this situation.

The exception handler immediately returns without calling terminate(), kill(), or a final communicate()/wait(). As a result, a hung engine can continue consuming CPU, GPU memory, system memory, handles, and other resources after the Skill reports a timeout.

The main game loop can invoke move calculation repeatedly. If the engine repeatedly hangs, each calculation can create another surviving process. The finally block only attempts to remove the temporary command file and does not clean up the process.

The subprocess uses a fixed executable path and an argument list without shell=True; no command-injection path was identified in this call. The confirmed issue is lifecycle and resource management.

Attack Path

  1. The KataGomo engine hangs because of a malformed model, engine defect, resource contention, or mal ...[truncated 973 chars]
Remediation
View remediation

Remediation Suggestions

Terminate and reap the child whenever a timeout occurs:

python
proc = None
try:
    with open(cmd_file, "r") as fin:
        proc = subprocess.Popen(
            [
                engine,
                "gtp",
                "-config",
                config,
                "-model",
                model,
                "-override-config",
                "basicRule=FREESTYLE",
            ],
            stdin=fin,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            cwd=KATAGOMO_DIR,
            creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
        )
        output, _ = proc.communicate(timeout=120)
except subprocess.TimeoutExpired:
    proc.terminate()
    try:
        proc.communicate(timeout=5)
    except subprocess.TimeoutExpired:
        proc.kill()
        proc.communicate()
    return None, None, None, "Engine timeout"

Additional hardening should include:

  1. Limit concurrent engine processes to one per game or Skill instance.
  2. Track process IDs and clean up children during shutdown and signal handling.
  3. Validate the engine, configuration, and model paths before execution.
  4. Verify the integrity or signature of the local engine binary and model.
  5. Apply operating-system resource limits or execute the engine in a constrained sandbox.
  6. Use subprocess.run(..., timeout=120, check=True) where persistent process control is unnecessary.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (24)

Tainted flow: 'req' from os.environ.get (line 137, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · gomoku_bot.py (reported line 51)May include surrounding context.

python
url = f'{BASE_URL}/activity'
    req = urllib.request.Request(url, headers={'Authorization': f'Bearer {API_KEY}'})
    try:
        response = urllib.request.urlopen(req, timeout=10)
        data = json.loads(response.read().decode('utf-8'))
        return data.get('data', {})
    except Exception as e:

Tainted flow: 'req' from os.environ.get (line 137, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · gomoku_bot.py (reported line 64)May include surrounding context.

python
url = f'{BASE_URL}/activity'
    req = urllib.request.Request(url, headers={'Authorization': f'Bearer {API_KEY}'})
    try:
        response = urllib.request.urlopen(req, timeout=10)
        data = json.loads(response.read().decode('utf-8'))
        return data.get('data', {})
    except Exception as e:

Tainted flow: 'req' from os.environ.get (line 137, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · gomoku_bot.py (reported line 83)May include surrounding context.

python
headers={'Authorization': f'Bearer {API_KEY}', 
                                        'Content-Type': 'application/json'})
    try:
        response = urllib.request.urlopen(req, timeout=15)
        result = json.loads(response.read().decode('utf-8'))
        if result.get('success'):
            room_id = result['data']['room']['id']

Tainted flow: 'req' from os.environ.get (line 137, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · gomoku_bot.py (reported line 108)May include surrounding context.

python
headers={'Authorization': f'Bearer {API_KEY}', 
                                        'Content-Type': 'application/json'})
    try:
        response = urllib.request.urlopen(req, timeout=15)
        result = json.loads(response.read().decode('utf-8'))
        if result.get('success'):
            room_id = result['data']['room']['id']

Tainted flow: 'req' from os.environ.get (line 137, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · gomoku_bot.py (reported line 141)May include surrounding context.

python
headers={'Authorization': f'Bearer {API_KEY}', 
                                        'Content-Type': 'application/json'})
    try:
        response = urllib.request.urlopen(req, timeout=15)
        result = json.loads(response.read().decode('utf-8'))
        if result.get('success'):
            room_id = result['data']['room']['id']

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill documentation exposes that it contains a hardcoded API key and relies on an external or undeclared AI component beyond the stated local gameplay behavior. This is risky because it combines secret handling failures with undeclared dependencies that may transmit data or execute code outside the user's expectations.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill documentation exposes that it contains a hardcoded API key and relies on an external or undeclared AI component beyond the stated local gameplay behavior. This is risky because it combines secret handling failures with undeclared dependencies that may transmit data or execute code outside the user's expectations.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill documentation exposes that it contains a hardcoded API key and relies on an external or undeclared AI component beyond the stated local gameplay behavior. This is risky because it combines secret handling failures with undeclared dependencies that may transmit data or execute code outside the user's expectations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code embeds a default API key directly in source and will use it automatically when the environment variable is unset. Anyone with access to the code can recover and abuse that credential for unauthorized API actions, and all users of the skill may unknowingly operate under the same shared secret.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

A hard-coded API key embedded in source code is a real secret-management vulnerability because anyone with access to the skill file can extract and reuse the credential. In this skill context, the key authorizes authenticated game API actions, enabling unauthorized access, impersonation, abuse of the associated account, and possible lateral exposure if the token is reused elsewhere.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill advertises executable behaviors such as shell commands, file access, environment access, and likely networked gameplay/API use, but does not declare any explicit tool scope or permissions. This makes the operational boundary opaque and increases the chance of over-privileged execution or accidental access beyond what a user would reasonably expect from a board-game helper skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The description does not clearly warn users that the skill may automatically submit moves during live gameplay. That omission is security-relevant because it hides account-affecting automation behind a benign-sounding AI helper description, reducing informed consent and increasing the chance of unintended actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Broad trigger phrases like '帮我下棋' or '执行五子棋对局' can activate the skill during ordinary conversation without sufficiently specific user intent. In this skill's context, that matters because the skill is described as capable of automatic gameplay actions, so accidental invocation could lead to unwanted account activity or move submission.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
68% confidence
Finding

This appears to be a duplicate session-persistence finding for the same location and behavior: documented create/auto modes imply ongoing automated game activity. Such persistence is risky in a game bot because it can keep using authenticated state and performing actions after the original user intent has passed.

Content

Scanner excerpt · SKILL.md (reported line 32)May include surrounding context.

bash
# 方式1: 创建房间并自动对弈
cd ~/.openclaw/workspace/skills/instreet-gomoku
python gomoku_bot.py create

# 方式2: 自动匹配
python gomoku_bot.py auto

Session Persistence

Medium
Category
Rogue Agent
Confidence
68% confidence
Finding

This appears to be a duplicate session-persistence finding for the same location and behavior: documented create/auto modes imply ongoing automated game activity. Such persistence is risky in a game bot because it can keep using authenticated state and performing actions after the original user intent has passed.

Content

Scanner excerpt · SKILL.md (reported line 32)May include surrounding context.

bash
# 方式1: 创建房间并自动对弈
cd ~/.openclaw/workspace/skills/instreet-gomoku
python gomoku_bot.py create

# 方式2: 自动匹配
python gomoku_bot.py auto

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The documentation directly exposes a hardcoded API key, which is a real secret disclosure issue. Anyone who can read the file may reuse the credential for unauthorized authenticated API access, impersonation, quota abuse, or persistence against the linked game service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Using a hardcoded fallback credential without clear user warning causes silent remote authentication under a built-in secret. This increases the chance of unauthorized use, accidental credential sharing across installations, and misuse by anyone who obtains the source.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill sends authenticated requests containing game activity and move data to a remote service without any evident user disclosure or consent mechanism. In an agent-skill setting, undisclosed outbound transmission is risky because users may assume local-only analysis while the skill actually shares board state and uses a credentialed backend.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest describes an AI that automatically computes the best move with threat-aware defense, which implies the skill's own implemented Gomoku logic. However, the exported interface first imports and calls KataGomo.get_best_move, only falling back to the local AI on failure, so actual behavior depends on an undeclared external engine rather than the in-file implementation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This Python file contains natural-language documentation entirely in Chinese, including the module docstring and later user-facing return messages, with no indication that the skill is region-specific or that users can opt into another language. Under the language/locale policy rule, forcing a specific language without user choice is a policy concern.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · katagomo_simple.py (reported line 113)May include surrounding context.

python
try:
            with open(cmd_file, 'r') as fin:
                proc = subprocess.Popen(
                    [engine, "gtp", "-config", config, "-model", model, "-override-config", "basicRule=FREESTYLE"],
                    stdin=fin,
                    stdout=subprocess.PIPE,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The entire skill description is written in Chinese and does not indicate that other languages are supported or that the Chinese-only presentation is intentional for a region-specific audience. Under the policy rule, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

All user-facing instructions, triggers, and operational documentation are presented only in Chinese, with no indication that other languages are supported or that this is an intentional region-specific limitation. Under the language/locale policy, a forced language without user opt-in can be a policy issue.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

get_best_move is documented and implemented in most branches as returning move coordinates, position, and reason, but when no move is found it returns only None, None, "No valid move". This contradicts the function's apparent interface and behavior in the success paths, creating intent/behavior divergence in the documented external API.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.