Back to skill

Security audit

stock-watchlist-aisa

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its watchlist purpose, but it can send the AISA API key and ticker checks to an arbitrary endpoint through an undocumented environment variable.

Review before installing. Only run this skill in an environment you control, leave AISA_BASE_URL unset unless you intentionally trust the endpoint, use a limited AISA key with spending limits, and consider pinning dependencies before using uv or another resolver.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/watchlist.py:55
Finding

API Credential Can Be Forwarded to an Arbitrary Configured Endpoint

Content
View full analysis
OpenAI: api_key = os.environ.get("AISA_API_KEY") if not api_key: print("❌ Error: AISA_API_KEY environment variable is not set.", file=sys.stderr) sys.exit(1) base_url = os.environ.get("AISA_BASE_URL", "https://api.aisa.one/v1") return OpenAI(api_key=api_key, base_url=base_url) ``` ### Technical Analysis The Skill retrieves the sensitive `AISA_API_KEY` credential and supplies it to an OpenAI-compatible client whose destination is controlled by the `AISA_BASE_URL` environment variable. The code does not enforce HTTPS, validate the destination hostname, or restrict the override to trusted AISA domains. When `check` is run, the client sends an authenticated request to the configured endpoint. Consequently, anyone capable of influencing the Skill's environment can redirect the API request to an attacker-controlled server. That server can capture the API authorization credential as well as the request body containing the user's watchlist ticker symbols. Live network access and authentication to the documented AISA service are necessary for the declared live-price functionality. Allowing the same credential to be forwarded to an unrestricted destination exceeds the minimum privilege needed for that functionality. ### Attack Path 1. An attacker influences the execution environment, deployment configuration, shell profile, wrapper script, or task configuration. 2. The attacker sets `AISA_BASE_URL` to an endpoint under their control, such as `https://attacker.example/v1`. 3. The user invokes `python3 scripts/watchlist.py check`. 4. `get_client()` constructs a client using the legitimate `AISA_API_KEY` but the attacker-controlled base URL. 5. The client sends an authenticated chat- ...[truncated 791 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/watchlist.py:3
Finding

Runtime Dependency Is Not Pinned to a Reviewed Version

Content
View full analysis
=3.10" # dependencies = [ # "openai>=1.0.0", # ] # /// ``` ### Technical Analysis The inline dependency declaration accepts every `openai` release from version 1.0.0 onward, without an upper bound, exact version, lockfile, or integrity hash. When the script is executed through a dependency-resolving runner such as the documented `uv run` examples in its module text, the resolved package can therefore change without any modification to the audited Skill. This reduces build reproducibility and creates a supply-chain trust risk. A compromised upstream release, maliciously published version, or future incompatible version could be installed and imported with the process's permissions. At runtime, the dependency has access to the API key passed to it and can observe the request data. The repository also presents inconsistent runtime guidance: `SKILL.md` identifies direct `python3` execution as canonical, while the script examples use `uv run`. Direct execution will fail unless the dependency has already been installed, potentially encouraging ad hoc dependency resolution. ### Attack Path 1. The user follows a `uv run watchlist.py ...` usage example or otherwise resolves the inline dependencies. 2. The resolver selects a newer `openai` version permitted by `openai>=1.0.0`. 3. A selected release has been compromised or contains malicious behavior. 4. The package is installed and imported by `scripts/watchlist.py`. 5. The dependency executes with the user's process permissions and receives the AISA credential when `OpenAI(api_key=...)` is instantiated. 6. Malicious dependency code could disclose the key, alter network requests, access files available to the process, or execute other actions under the invoking user's accou ...[truncated 750 chars]
Remediation
View remediation
", # ] ``` 2. Maintain a lockfile containing exact transitive dependency versions. 3. Use integrity hashes for downloaded packages where the installation workflow supports them. 4. Update dependencies through a controlled review process that includes security scanning and compatibility testing. 5. Align `SKILL.md` and the script's usage examples so users follow one documented, reproducible installation and execution path. 6. Install dependencies from the official package index or another explicitly trusted registry, and prevent untrusted index configuration from taking precedence. 7. Run the Skill with only the environment variables and filesystem permissions required for watchlist operation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared behavior understates the actual capabilities by omitting removal operations, signal generation, and persisted signal-change alert state. This mismatch weakens informed consent and reviewability: users and policy systems may authorize a simple watchlist checker while the skill performs broader analysis and stateful actions than advertised.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
## When NOT to Use

- Do not use this skill for browser-cookie extraction, passwords, Keychain access, or other local sensitive credential access.
- Prefer a different skill when the user request is outside this skill's domain.

## Capabilities

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares access to environment variables and clearly describes persistent local file storage, but it does not define any explicit tool scope such as permissions or allowed-tools. That creates an authorization gap where the runtime may grant broader file or environment access than users expect, increasing the chance of unintended data exposure or unsafe file modification.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/watchlist.py (reported line 60)May include surrounding context.

python
if not api_key:
        print("❌ Error: AISA_API_KEY environment variable is not set.", file=sys.stderr)
        sys.exit(1)
    base_url = os.environ.get("AISA_BASE_URL", "https://api.aisa.one/v1")
    return OpenAI(api_key=api_key, base_url=base_url)

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest sets "language": "en", which is a natural-language locale constraint. Under the policy, forcing a specific language without user opt-in can be a language/locale policy violation, and there is no indication elsewhere in this file that users can choose another language or that the restriction is justified.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.