Back to skill

Security audit

Token Usage Tracker

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its token-tracking purpose, but review is warranted because its log-maintenance scripts can unsafely rewrite files in some local deployments.

Install only after reviewing the scripts and setting the log directory to a private, trusted workspace path. Do not run the migration or dedupe utilities as root or from a directory writable by other users unless the temporary-file handling is hardened. Treat the systemd file as a template and change the user and paths before enabling it.

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/migrate_timestamps.py:21
Finding
Predictable Temporary File Enables Symlink-Based File Overwrite During Timestamp Migration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/migrate_timestamps.py`, lines 21–43 **Vulnerability Type**: Predictable temporary file and symbolic-link following **Risk Level**: Medium ```python def migrate(): if not LOG_PATH.exists(): print('No log found at', LOG_PATH) return tmp = LOG_PATH.with_suffix('.tmp') count = 0 with LOG_PATH.open('r',encoding='utf-8') as fin, tmp.open('w',encoding='utf-8') as fout: for line in fin: try: obj = json.loads(line) ts = obj.get('timestamp','') if isinstance(ts,str) and ts.endswith('Z'): s = ts.replace('Z','+00:00') try: dt = datetime.fromisoformat(s) obj['timestamp'] = dt.astimezone(TZ).isoformat() count += 1 except Exception: pass fout.write(json.dumps(obj)+"\n") except Exception: fout.write(line) tmp.replace(LOG_PATH) ``` ### Technical Analysis The migration utility derives its temporary filename predictably by replacing the log suffix with `.tmp`. It then opens that path using regular write mode: ```python tmp.open('w', encoding='utf-8') ``` This operation neither creates the file exclusively nor prevents symbolic-link traversal. If an attacker can write to the configured log directory, the attacker can create `token_log.tmp` as a symbolic link to another file before the migration starts. Python's normal file-opening behavior follows that link, opens the linked target in write mode, and truncates it. After writing, `tmp.replace(LOG_PATH)` renames the temporary directory entry over the original log. If the temporary entry is a symbolic link, the link itself is renamed; however, the linked target has already been truncated and overwritten during the preceding write operation. Exploitation requires loca ...[truncated 1409 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create temporary files securely and unpredictably in the same directory as the destination, using `tempfile.NamedTemporaryFile` or `tempfile.mkstemp`. - Do not reuse a fixed temporary pathname. - Use restrictive file permissions, such as mode `0600`, for token logs and temporary files. - Flush buffered data and call `os.fsync()` before replacing the destination. - Complete the update with `os.replace()` so that replacement remains atomic. - Verify that the configured log directory is owned by the service account and is not writable by untrusted users. - Where supported, use no-follow semantics such as `O_NOFOLLOW` and verify with `fstat()` that the opened object is a regular file. - Validate that `LOG_PATH` and its parent directory are not symbolic links when operating across trust boundaries. A hardened pattern is: ```python import os import tempfile with LOG_PATH.open('r', encoding='utf-8') as fin: fd, tmp_name = tempfile.mkstemp( prefix='.token_log.', suffix='.tmp', dir=LOG_PATH.parent ) try: with os.fdopen(fd, 'w', encoding='utf-8') as fout: # Perform migration and write output. fout.flush() os.fsync(fout.fileno()) os.replace(tmp_name, LOG_PATH) except Exception: try: os.unlink(tmp_name) except FileNotFoundError: pass raise ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dedupe_log.py:11
Finding
Predictable Temporary File Enables Symlink-Based File Overwrite During Log Deduplication<![CDATA[ ## Vulnerability Details **File Location**: `scripts/dedupe_log.py`, lines 11–29 **Vulnerability Type**: Predictable temporary file and symbolic-link following **Risk Level**: Medium ```python def dedupe(): if not LOG_PATH.exists(): print('No log at', LOG_PATH) return seen = set() tmp = LOG_PATH.with_suffix('.dedupe') with LOG_PATH.open('r',encoding='utf-8') as fin, tmp.open('w',encoding='utf-8') as fout: for line in fin: try: j = json.loads(line) cid = j.get('call_id') key = cid if cid else line if key in seen: continue seen.add(key) fout.write(json.dumps(j)+"\n") except Exception: fout.write(line) tmp.replace(LOG_PATH) ``` ### Technical Analysis The deduplication utility always uses the predictable temporary path `token_log.dedupe`. It opens that path with write mode without exclusive creation, symbolic-link rejection, or validation of the opened file type. An attacker who can create files in the log directory can pre-create `token_log.dedupe` as a symbolic link. When the script runs, `tmp.open('w')` follows the link and truncates the linked target before writing deduplicated log data. The later replacement operation does not reverse the damage to the target. This is a time-of-check/time-of-use and unsafe temporary-file issue across a local trust boundary. Exploitation is conditional on the attacker having write access to the log directory while the script runs under an account that can write to a more valuable target. ### Attack Path 1. The attacker gains write access to the directory containing `token_log.jsonl`. 2. The attacker creates `token_log.dedupe` as a symbolic link to a selected victim file. 3. The deduplication utility is invoked by a user or service with permission to write to that victim file. 4. The utility follows the link and ope ...[truncated 760 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace the fixed `.dedupe` filename with a securely generated, unpredictable temporary file in `LOG_PATH.parent`. - Create the temporary file atomically with exclusive creation through `tempfile.mkstemp` or `NamedTemporaryFile`. - Set restrictive permissions and ensure the log directory is not writable by untrusted users. - Flush and synchronize the completed file before atomic replacement. - Use `os.replace()` only after all writes have succeeded. - Remove the temporary file safely when processing fails. - Reject symbolic links and non-regular files where no-follow file-opening support is available. - Consider adding file locking if multiple tracker or maintenance processes may modify the log concurrently. A secure implementation should follow this sequence: 1. Open the source log. 2. Securely create a randomized temporary file in the same directory. 3. Write the deduplicated records. 4. Flush and call `os.fsync()`. 5. Close the temporary file. 6. Atomically replace the original using `os.replace()`. 7. Delete the temporary file on every failure path. ]]>
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises file-reading and file-writing functionality through its described scripts and log handling, but it does not declare any tool scope such as permissions or allowed-tools. This creates a trust and review gap: consumers cannot easily determine the intended file-system access boundaries, increasing the risk of overbroad access, unsafe deployment, or misuse if the examples are wired into an agent pipeline without restrictions.

Static analysis

No suspicious patterns detected.