Back to skill

Security audit

Ghostfolio

Security checks for vulnerabilities and agentic risk

Overview

This Ghostfolio helper matches its purpose, but it needs review because it handles a long-lived financial-data token and includes unsafe remote/TLS guidance.

Install only if you will configure GHOSTFOLIO_BASE_URL yourself and keep it to localhost or a verified HTTPS Ghostfolio server. Do not use curl -k with real tokens; fix certificate trust instead. Rotate the Ghostfolio token if it may have been sent to the wrong host, and avoid leaving probe response files in /tmp.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:15
Finding

Long-Lived Authentication Token Can Be Sent to an Untrusted Configurable Endpoint

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:15-17, SKILL.md:39-42, and SKILL.md:93-96
Vulnerability Type: Credential exposure through insufficient endpoint validation
Risk Level: High

The skill defines a user-configurable base URL and subsequently sends the long-lived Ghostfolio token to that URL. It neither validates the destination hostname nor requires HTTPS for non-loopback destinations.

bash
# Prefer local access when available
export GHOSTFOLIO_BASE_URL="http://127.0.0.1:3333"
# Optional remote example:
# export GHOSTFOLIO_BASE_URL="https://rpi5.gate-mintaka.ts.net:8444"

The anonymous exchange sends the long-lived token in a request body:

bash
AUTH_TOKEN=$(curl -fsS "$GHOSTFOLIO_BASE_URL/api/v1/auth/anonymous" \
  -H 'Content-Type: application/json' \
  --data "{\"accessToken\":\"$GHOSTFOLIO_TOKEN\"}" \
| jq -r '.authToken')

The direct authentication probe sends it as a bearer token:

bash
code=$(curl -s -o /tmp/gf_probe.json -w '%{http_code}' "$GHOSTFOLIO_BASE_URL$ep" \
  -H "Authorization: Bearer $GHOSTFOLIO_TOKEN" \
  -H 'Accept: application/json' \
  -H "x-ghostfolio-timezone: $GHOSTFOLIO_TIMEZONE")

Technical Analysis

GHOSTFOLIO_BASE_URL determines the server receiving both authentication requests and authenticated API calls. No hostname allowlist, URL-scheme validation, trust confirmation, or loopback-only restriction is applied before the long-lived token is transmitted.

Although plaintext HTTP is appropriate for the documented IPv4 loopback address when the service is local, the same variable can contain an arbitrary non-loopback HTTP or HTTPS destination. The remote example also uses a project-specific hostname rather than clearly limiting remote access to an administrator-verified Ghostfolio instance.

Consequently, an incorrectly configured or attacker-influenced environment value can direct the authentication material to a server out ...[truncated 1145 chars]

Remediation
View remediation

Remediation Suggestions

  • Permit plaintext HTTP only when the parsed destination is an explicit loopback address such as 127.0.0.1 or ::1.
  • Require HTTPS for every non-loopback destination.
  • Validate the destination hostname against an administrator-controlled allowlist before transmitting credentials.
  • Require explicit user confirmation when the endpoint differs from the expected Ghostfolio host.
  • Remove the project-specific remote hostname from the generic skill example or clearly mark it as a placeholder that must be replaced and independently verified.
  • Recommend short-lived, narrowly scoped, and revocable API credentials rather than long-lived tokens.
  • Avoid inheriting security-sensitive endpoint values from untrusted execution environments.
  • Document token rotation and immediate revocation procedures for suspected disclosure.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:121
Finding

TLS Certificate Verification Bypass Recommended for Authenticated Requests

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:121-123
Vulnerability Type: Improper certificate validation guidance
Risk Level: Medium

The troubleshooting guidance recommends temporarily disabling TLS certificate verification:

markdown
- Connectivity issues
  - Prefer local URL (`http://127.0.0.1:3333`) if service runs locally.
  - For remote TLS diagnostics only, temporary `curl -k` can help.

Technical Analysis

The curl -k option, also known as --insecure, disables validation of the remote server's TLS certificate and hostname. Encryption without authentication does not establish that the client is communicating with the intended Ghostfolio server.

This is particularly hazardous in this skill because requests carry a long-lived bearer token or exchange that token for an authentication token. A user may also forget to remove -k after diagnostics, causing the unsafe behavior to persist in copied commands or automation.

Attack Path

  1. A user encounters a certificate validation error while accessing a remote Ghostfolio server.
  2. Following the troubleshooting advice, the user adds -k to an authenticated curl request.
  3. An attacker able to intercept or redirect network traffic presents an arbitrary certificate.
  4. Because certificate and hostname verification are disabled, curl accepts the attacker's endpoint.
  5. The authenticated request, including the bearer token or token-exchange body, is delivered to the attacker.
  6. The attacker may steal the credential, observe financial data, or return forged API responses.

Impact Assessment

An attacker in a suitable network or routing position can impersonate the Ghostfolio server. This may expose authentication tokens and sensitive portfolio responses or allow response manipulation. Any subsequent unauthorized Ghostfolio access is limited to the permissions of the intercepted token.

Remediation
View remediation

Remediation Suggestions

  • Remove the recommendation to use curl -k or --insecure.
  • Diagnose TLS failures with non-bypassing tools and options such as curl -v, certificate inspection, and server-chain verification.
  • Install the correct trusted certificate authority in the system trust store.
  • For private certificate authorities, use curl --cacert /trusted/path/ca.pem with an independently verified CA certificate.
  • Verify that the certificate's subject alternative name matches the configured hostname.
  • If certificate pinning is operationally appropriate, document a securely distributed public-key pin rather than disabling verification.
  • Never submit a real token while diagnosing a server whose identity has not been authenticated.

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:88
Finding

Predictable Shared Temporary File Used for Authenticated API Responses

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:88-98
Vulnerability Type: Unsafe temporary-file handling
Risk Level: Low

The connectivity probe repeatedly writes authenticated API responses to a fixed path in the shared /tmp directory:

bash
# 1) Try direct bearer first
for ep in \
  '/api/v2/portfolio/performance?range=ytd' \
  '/api/v1/portfolio/holdings?range=ytd' \
  '/api/v1/portfolio/dividends?groupBy=month&range=ytd'
do
  code=$(curl -s -o /tmp/gf_probe.json -w '%{http_code}' "$GHOSTFOLIO_BASE_URL$ep" \
    -H "Authorization: Bearer $GHOSTFOLIO_TOKEN" \
    -H 'Accept: application/json' \
    -H "x-ghostfolio-timezone: $GHOSTFOLIO_TIMEZONE")
  echo "direct $ep -> $code"
done

Technical Analysis

/tmp/gf_probe.json is predictable and shared across invocations. The command does not securely create the file, verify its type or ownership, set restrictive permissions explicitly, or remove it afterward.

On systems where applicable filesystem protections do not prevent the operation, another local user may pre-create the path or arrange a symbolic-link attack. Concurrent probes can also overwrite each other's output. Moreover, the resulting file may retain sensitive portfolio responses after the connectivity test completes.

Attack Path

  1. A local attacker predicts the fixed path /tmp/gf_probe.json.
  2. Before the victim runs the probe, the attacker creates a conflicting path or symbolic link where platform protections permit it.
  3. The victim runs the documented authenticated probe.
  4. curl follows or overwrites the prepared destination using the victim's filesystem privileges.
  5. This may overwrite another file writable by the victim, interfere with the probe, or leave sensitive API response data at a known location.
  6. The fixed file remains after execution unless the user manually removes it.

Impact Assessment

The principal impact is local exposure o ...[truncated 308 chars]

Remediation
View remediation

Remediation Suggestions

  • Because the probe only needs the HTTP status, replace the output path with -o /dev/null.
  • If the response body must be retained, create a private temporary file with mktemp.
  • Set a restrictive process umask, such as umask 077, before creating files containing portfolio data.
  • Register a cleanup trap to delete the temporary file on normal exit, interruption, or error.
  • Do not reuse a predictable filename across users or concurrent invocations.
  • Validate that any retained temporary object is a regular file owned by the current user.
  • Prefer keeping diagnostic response data in memory when practical.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Recommending curl -k disables TLS certificate validation, making HTTPS connections vulnerable to man-in-the-middle interception and spoofed servers. In this skill, that becomes more dangerous because authentication tokens are used against API endpoints, so bypassing TLS verification can directly expose credentials and portfolio data.

Content

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

md
- Connectivity issues
  - Prefer local URL (`http://127.0.0.1:3333`) if service runs locally.
  - For remote TLS diagnostics only, temporary `curl -k` can help.

## Safety Notes

External Transmission

Medium
Category
Data Exfiltration
Confidence
92% confidence
Finding

The skill instructs sending the long-lived GHOSTFOLIO_TOKEN to an HTTP endpoint as JSON during the anonymous auth exchange. Because the documented default base URL is plain HTTP on localhost and the skill also permits remote base URLs, this can expose a sensitive bearer credential to network interception, local proxying, or SSRF-style redirection if the base URL is altered.

Content

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

Mode B — Anonymous exchange (required in some environments)

bash
AUTH_TOKEN=$(curl -fsS "$GHOSTFOLIO_BASE_URL/api/v1/auth/anonymous" \
  -H 'Content-Type: application/json' \
  --data "{\"accessToken\":\"$GHOSTFOLIO_TOKEN\"}" \
| jq -r '.authToken')

External Transmission

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The connectivity probe sends the raw GHOSTFOLIO_TOKEN as a Bearer token to multiple endpoints, which transmits the credential to whatever host is configured in GHOSTFOLIO_BASE_URL. In context, this is risky because the skill encourages configurable local or remote URLs, so a misconfigured or malicious endpoint could capture the token during troubleshooting.

Content

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

md
'/api/v1/portfolio/holdings?range=ytd' \
  '/api/v1/portfolio/dividends?groupBy=month&range=ytd'
do
  code=$(curl -s -o /tmp/gf_probe.json -w '%{http_code}' "$GHOSTFOLIO_BASE_URL$ep" \
    -H "Authorization: Bearer $GHOSTFOLIO_TOKEN" \
    -H 'Accept: application/json' \
    -H "x-ghostfolio-timezone: $GHOSTFOLIO_TIMEZONE")