Back to skill

Security audit

Kaspa Wallet

Security checks for vulnerabilities and agentic risk

Overview

This is a real cryptocurrency wallet skill, but it can immediately spend funds and auto-install unpinned code, so users should review it carefully before use.

Install only if you are comfortable giving an agent wallet-spending capability. Prefer testnet or small balances first, do not expose real private keys or mnemonics to broad agent sessions, pin and review dependencies before use, and add a mandatory confirmation or dry-run workflow before allowing mainnet sends.

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)

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unpinned Security-Critical Dependency Creates a Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1, with installation behavior in install.py:138-149 and automatic bootstrap in kaswallet.sh:43-44
Vulnerability Type: Unpinned executable third-party dependency
Risk Level: Medium

Vulnerable Code

requirements.txt:1:

text
kaspa

install.py:138-149:

python
log("Installing dependencies...")
pip_cmd = [str(venv_python), "-m", "pip", "install", "--upgrade", "pip"]

try:
    run_command(pip_cmd, capture=True)
except subprocess.CalledProcessError:
    log("Warning: Could not upgrade pip, continuing anyway...")

pip_install = [str(venv_python), "-m", "pip", "install", "-r", str(REQ_FILE)]

try:
    run_command(pip_install)

kaswallet.sh:43-44:

bash
echo "Python venv not found. Bootstrapping with: $PY install.py" >&2
"$PY" install.py >&2

Technical Analysis

The wallet installs kaspa without an exact version constraint or package integrity hash. Consequently, each fresh installation resolves whichever compatible package release is available from the configured Python package index at that time.

This dependency is imported into a process that handles KASPA_PRIVATE_KEY or KASPA_MNEMONIC, derives wallet keys, builds transactions, and signs payments. A compromised or unexpectedly modified package release would therefore execute within a highly sensitive security context.

The launcher increases exposure by automatically invoking the installer whenever the local virtual environment is missing. A command that appears to run the wallet can therefore first download and execute newly resolved third-party package code.

Attack Path

  1. An attacker compromises the upstream package, its publishing credentials, the configured package index, or a dependency selected during resolution.
  2. A user runs python3 install.py, or invokes kaswallet.sh while .venv is absent.
  3. Pip resolves and insta ...[truncated 838 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin kaspa and all transitive dependencies to reviewed, exact versions.
  2. Generate a lock file containing cryptographic hashes and install with pip's --require-hashes option.
  3. Review package provenance, publisher identity, release signatures, and dependency changes before updating the lock file.
  4. Separate installation from wallet execution. If .venv is missing, fail with explicit instructions instead of automatically downloading executable dependencies.
  5. Use a controlled package repository or verified artifact mirror for production wallet deployments.
  6. Add automated dependency vulnerability and integrity scanning to the release process.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/kaswallet.py:53
Finding

Cryptocurrency Amounts Are Converted Using Binary Floating-Point Arithmetic

Content
View full analysis

Vulnerability Details

File Location: scripts/kaswallet.py:53-54; the conversion is used for transaction amounts and priority fees at scripts/kaswallet.py:403, scripts/kaswallet.py:434, scripts/kaswallet.py:483, and scripts/kaswallet.py:508
Vulnerability Type: Financial amount precision and validation weakness
Risk Level: Medium

Vulnerable Code

scripts/kaswallet.py:53-54:

python
def kas_to_sompi(kas: str) -> int:
    return int(round(float(kas) * 1e8))

Representative transaction use at scripts/kaswallet.py:483-508:

python
priority_fee = kas_to_sompi(args.priority_fee) if args.priority_fee else 0

fee_rate = 1.0
tier = args.tier or "economic"
try:
    resp = await c.get_fee_estimate()
    estimate = resp.estimate if hasattr(resp, "estimate") else resp.get("estimate")
    fee_rate, _ = _pick_feerate(estimate, tier)
except Exception:
    fee_rate = 1.0

to_addr = sanitize_address_input(args.to)

if args.amount == "max":
    max_info = await _estimate_max_amount(
        kaspa,
        utxos=utxos,
        to=to_addr,
        balance=bal,
        change_address=addr,
        fee_rate=fee_rate,
        priority_fee=priority_fee,
    )
    amount = int(max_info["amount"])
else:
    amount = kas_to_sompi(args.amount)

Technical Analysis

KAS values are decimal financial quantities with a fixed conversion of 100,000,000 sompi per KAS. The implementation first converts user-controlled text into an IEEE-754 binary floating-point value, multiplies it by 1e8, and rounds it.

Many decimal fractions cannot be represented exactly as binary floating-point values. Large values also lose integer precision. In addition, inputs containing more than eight fractional decimal places are silently rounded rather than rejected. The resulting integer may therefore differ from the exact amount represented by the user's input.

The same conversion routine co ...[truncated 1105 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse amounts with decimal.Decimal rather than float.
  2. Reject non-finite values such as NaN and infinity.
  3. Require a canonical decimal format and reject inputs with more than eight fractional digits instead of silently rounding.
  4. Convert exactly by multiplying the validated Decimal by Decimal(100000000) and requiring an integral result.
  5. Enforce explicit minimum, maximum, and positive-value constraints before transaction construction.
  6. Add unit tests covering rounding boundaries, very large values, exponent notation, negative values, non-finite values, and values with more than eight decimal places.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/kaswallet.py:670
Finding

Unknown Command-Line Arguments Are Silently Ignored

Content
View full analysis

Vulnerability Details

File Location: scripts/kaswallet.py:670-672
Vulnerability Type: Fail-open command-line argument parsing
Risk Level: Low

Vulnerable Code

python
async def main_async(argv: list[str]) -> int:
    parser = build_parser()
    ns, _ = parser.parse_known_args(argv)

Technical Analysis

parse_known_args() returns recognized arguments and a separate collection of unrecognized arguments. The implementation assigns that collection to _ and never validates it.

As a result, misspelled options, unsupported safety controls, or accidental excess arguments do not stop execution. This is particularly risky in a transaction-signing command because callers may believe that an additional parameter was accepted and applied even though the wallet silently discarded it.

This does not directly allow arbitrary command execution. It is a transaction-integrity and automation-safety weakness caused by failing open on malformed invocations.

Attack Path

  1. A user or automated system constructs an otherwise valid send command with a misspelled or unsupported argument.
  2. The parser recognizes the valid portion of the command and places the unknown token in the ignored remainder.
  3. The wallet proceeds without reporting that the requested parameter had no effect.
  4. A transaction may be estimated, signed, and submitted under assumptions that differ from the wallet's actual behavior.

Impact Assessment

No additional system privileges are obtained. The impact is confined primarily to command integrity and wallet operations. Users or integrations can unintentionally authorize transactions with unexpected settings, including different fee behavior or omitted caller-intended controls. Because submitted blockchain transactions may be irreversible, even a low-complexity parsing error can have financial consequences.

Remediation
View remediation

Remediation Suggestions

  1. Replace parse_known_args(argv) with parse_args(argv) so unknown arguments cause an immediate error.
  2. If compatibility requires parse_known_args(), explicitly verify that the unknown-argument list is empty and terminate otherwise.
  3. Return a structured JSON error containing the rejected arguments without initiating RPC or wallet operations.
  4. Add tests for misspelled options, unexpected positional arguments, duplicated parameters, and extra tokens on the send and estimate commands.
  5. Ensure all validation completes before loading wallet credentials, connecting to RPC, constructing transactions, or signing data.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill is presented as a wallet for balances and payments, but the documented behavior also installs software, creates a virtual environment, and modifies local directories. That mismatch increases supply-chain and host-modification risk because an agent or user expecting wallet actions may unknowingly run setup commands that fetch code from the internet and alter the local system.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The send path signs and submits transactions immediately once the command is invoked, without an interactive confirmation, dry-run preview, or explicit acknowledgment of recipient, amount, and fees. In an agent skill context, this is especially dangerous because a prompt-influenced or mistaken invocation can directly move cryptocurrency irreversibly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README gives direct instructions for sending cryptocurrency and configuring private keys but does not warn that transfers are irreversible or that secrets must be handled carefully. In an agent-facing skill, this increases the chance of accidental fund loss or secret exposure because operators may follow the examples verbatim without understanding the risk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill invokes shell commands and depends on environment-provided secrets, but the manifest does not declare tool scope or permissions. In an agent setting, this weakens review boundaries and can allow a caller to trigger shell execution or secret-dependent operations without clear, auditable authorization expectations.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

md
**Troubleshooting install:**
- If pip fails: `pip install kaspa` manually, or try `KASPA_PYTHON=python3.12 python3 install.py`
- If venv missing: `sudo apt install python3-venv` (Ubuntu/Debian)
- To reinstall: `rm -rf .venv && python3 install.py`

## Environment Variables

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · install.py (reported line 56)May include surrounding context.

python
**Troubleshooting install:**
- If pip fails: `pip install kaspa` manually, or try `KASPA_PYTHON=python3.12 python3 install.py`
- If venv missing: `sudo apt install python3-venv` (Ubuntu/Debian)
- To reinstall: `rm -rf .venv && python3 install.py`

## Environment Variables

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · install.py (reported line 114)May include surrounding context.

python
**Troubleshooting install:**
- If pip fails: `pip install kaspa` manually, or try `KASPA_PYTHON=python3.12 python3 install.py`
- If venv missing: `sudo apt install python3-venv` (Ubuntu/Debian)
- To reinstall: `rm -rf .venv && python3 install.py`

## Environment Variables

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill instructs users to place a private key or mnemonic in environment variables without a prominent warning that these credentials fully control funds and may be exposed through shell history, process inspection, logs, crash reports, or inherited environments. In a cryptocurrency wallet context, compromise of these values can lead to immediate, irreversible theft.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The send-payment documentation does not clearly warn that blockchain transfers are real-value transactions that are generally irreversible once broadcast and confirmed. In an agent-assisted workflow, lack of this warning increases the chance of accidental transfers, wrong-address sends, or unintended use of 'max' behavior with permanent financial loss.

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 · install.py (reported line 89)May include surrounding context.

python
def run_command(cmd: list[str], capture: bool = False) -> subprocess.CompletedProcess:
    """Run a command with proper error handling."""
    try:
        return subprocess.run(
            cmd,
            cwd=str(ROOT),
            capture_output=capture,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script automatically consumes wallet secrets from environment variables and then enables spending operations without any user-facing disclosure that sensitive credentials are being loaded. In an agent-integrated wallet skill, this lowers the barrier to unintended signing because the presence of ambient credentials can turn a simple tool call into a live fund transfer capability.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The dependency is unpinned, so installs may resolve to different versions over time, including breaking or compromised upstream releases. In a cryptocurrency wallet skill, this is more dangerous than usual because dependency changes can affect key handling, transaction creation, address generation, or network interactions, potentially leading to fund loss or supply-chain compromise.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
kaspa

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/kaswallet.py (reported line 45)May include surrounding context.

python
return obj[name]
        else:
            if hasattr(obj, name):
                return getattr(obj, name)
    return default

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/kaswallet.py (reported line 91)May include surrounding context.

python
# First, try to get string from Address objects using to_string() method
    if not isinstance(value, str) and value is not None:
        for name in ("to_string", "toString", "as_string", "asString"):
            fn = getattr(value, name, None)
            if fn:
                try:
                    s = fn()

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/kaswallet.py (reported line 125)May include surrounding context.

python
s = sanitize_address_input(value)

    for name in ("try_from", "tryFrom", "from_string", "fromString", "parse"):
        fn = getattr(Address, name, None)
        if fn:
            return fn(s)

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/kaswallet.py (reported line 191)May include surrounding context.

python
def address_to_string(kaspa: Any, addr_obj: Any) -> str:
    for name in ("to_string", "toString", "as_string", "asString"):
        fn = getattr(addr_obj, name, None)
        if fn:
            try:
                return sanitize_address_input(fn())

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/kaswallet.py (reported line 222)May include surrounding context.

python
def call_any(obj: Any, names: list[str], *args: Any) -> Any:
        for name in names:
            fn = getattr(obj, name, None)
            if fn:
                return fn(*args)
        raise AttributeError(f"Missing method(s): {', '.join(names)}")

Static analysis

No suspicious patterns detected.