Back to skill

Security audit

Bobine Contract Caller

Security checks for vulnerabilities and agentic risk

Overview

The skill generally does what it claims, but its signed-call workflow exposes reusable private keys through command-line arguments and includes an unbounded CPU-work option.

Review before installing if you plan to use signed calls. Use only low-scope or disposable Bobine auth keys, avoid pasting private keys into shared terminals, CI logs, or shell history, and keep `--spark-effort` to known small values. Unsigned calls and local param packing appear coherent with the skill purpose.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/call_bobine.mjs:5
Finding

Ed25519 Private Key Exposed Through Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: scripts/call_bobine.mjs:5, 26-34; related command interface in scripts/lib/cli.mjs:49-54
Vulnerability Type: Sensitive credential exposure through process arguments
Risk Level: Medium

Vulnerable Code:

js
const options = parseOptions(process.argv.slice(2))
js
const result = options["auth-module"]
  ? await signEd25519Call(
      options["auth-module"],
      options.module,
      options.method,
      params,
      options.sigkey,
      options.pubkey,
      callOptions,
    )
  : await execute(options.module, options.method, params, callOptions)

The documented command interface explicitly accepts the private key as a command-line value:

js
[--auth-module <ed25519-module>] \
[--sigkey <private-ed25519-pkcs8-hex>] \
[--pubkey <public-ed25519-spki-hex>] \

Technical Analysis

The application reads the complete Ed25519 PKCS#8 private key from process.argv through the --sigkey option. Although the implementation uses the key locally and does not place it in the HTTP request, command-line arguments are not an appropriate secret transport mechanism.

Depending on the operating system and execution environment, command lines can be exposed through process inspection facilities, process-monitoring software, audit systems, CI/CD job logs, shell tracing, terminal recordings, command history, or diagnostic output. Any component that records the invocation can therefore retain the complete reusable signing key.

This issue is especially significant because the input is a long-lived private key rather than a single-use signature or token. Possession of the private key allows an attacker to create new signatures independently of the original process.

Attack Path

  1. A user follows the supported signed-call workflow and invokes call_bobine.mjs with the private key supplied through --sigkey.

...[truncated 1334 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove support for passing private keys directly through --sigkey.
  • Prefer a --sigkey-file option that reads the key from a permission-restricted file. Verify that the file is a regular file and, where supported, reject permissions that allow access by other users.
  • Alternatively, read the key from standard input or a dedicated secret-manager integration. Avoid prompting through ordinary echoed terminal input.
  • If environment-variable support is added, clearly document that environment variables may also be visible to process supervisors, crash reporters, or deployment tooling.
  • Ensure that validation errors, debug logs, telemetry, and exception handlers never include the private-key value.
  • Update all examples and usage text so users are not encouraged to place private keys in shell history.
  • Recommend rotation or revocation of keys that have already been used through command-line arguments in shared, monitored, or logged environments.
  • Clear references to sensitive buffers when practical after signing, while recognizing that JavaScript does not guarantee complete memory erasure.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/lib/effort.mjs:3
Finding

Unbounded Spark-Effort Input Can Cause Indefinite CPU Consumption

Content
View full analysis

Vulnerability Details

File Location: scripts/lib/bobine.mjs:19-21; generation loop in scripts/lib/effort.mjs:3-18
Vulnerability Type: Uncontrolled resource consumption
Risk Level: Medium

Vulnerable Code:

js
const effortRaw = sparkEffort ?? DEFAULT_SPARK_EFFORT.toString()
const target = BigInt(effortRaw)
const sparkBytes = await generateSpark(target)
js
export async function generateSpark(target) {
  const targetBig = BigInt(target)
  const max = 2n ** 256n

  while (true) {
    const effort = new Uint8Array(randomBytes(32))
    const hash = createHash("sha256").update(effort).digest("hex")
    const hashInt = BigInt(`0x${hash}`)

    if (hashInt === 0n) continue

    const value = max / hashInt
    if (value < targetBig) continue

    return effort
  }
}

Technical Analysis

The user-controlled --spark-effort value is converted to a BigInt without a permitted range check and passed to an unconditional while (true) proof-of-work loop. There is no maximum iteration count, deadline, cancellation signal, or periodic yield.

The computed value is:

text
floor(2^256 / SHA256(candidate))

Because a nonzero SHA-256 value is used as the divisor, the maximum possible result is 2^256. Consequently, any target greater than 2^256 is mathematically impossible to satisfy, making the loop nonterminating unless the process is externally killed. Targets at or below that boundary can still require impractical amounts of computation.

The function is declared async, but it contains no await inside the loop. It therefore continuously blocks the JavaScript event loop while searching, preventing timers or ordinary asynchronous cancellation logic from running in the same process.

Attack Path

  1. An attacker or untrusted automation input influences the arguments used to invoke the Skill.
  2. The attacker supplies an excessive value, such as a `--s ...[truncated 1148 chars]
Remediation
View remediation

Remediation Suggestions

  • Validate --spark-effort before starting generation. Require a positive integer and enforce a conservative application-specific maximum substantially below 2^256.
  • Reject impossible targets greater than 2^256 explicitly, even if the operational maximum is lower.
  • Add a configurable deadline and maximum iteration count. Return a clear failure when either limit is reached.
  • Support cancellation through an AbortSignal or equivalent mechanism.
  • Periodically yield control to the event loop or move proof-of-work generation to a worker thread so the main process remains responsive.
  • Apply execution-level CPU and wall-clock limits when the Skill runs in automated or multi-tenant environments.
  • Restrict untrusted instructions and remote data from directly controlling computational-effort arguments.
  • Add tests covering zero, negative, malformed, operationally excessive, and mathematically impossible effort values.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (1)

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The manifest describes a skill for calling deployed Bobine modules with typed params or performing signed Ed25519 calls through an auth module. This script instead generates a brand-new Ed25519 keypair and prints the private key material, which is a credential provisioning capability rather than contract invocation behavior.

Content

No source excerpt is available for this finding.