Back to skill

Security audit

Token Tamer — AI API Cost Control

Security checks for vulnerabilities and agentic risk

Overview

This cost-control skill is purpose-aligned, but it needs Review because its budget controls can under-report spending and its local logs may expose usage details.

Review this carefully before relying on it as a hard spending limit. It may be useful for local cost visibility, but keep provider-side budgets or billing alerts enabled, protect the JSON usage file, avoid storing secrets or customer data in task/session/metadata fields, and treat its kill switch as advisory unless the accounting flaws are fixed.

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
token_tamer.py:83
Finding
Negative Token Counts Can Reduce Recorded Spending and Bypass Budget Enforcement<![CDATA[ ## Vulnerability Details **File Location**: `token_tamer.py:83-101`, `token_tamer.py:218-244`, `token_tamer.py:326-327` **Vulnerability Type**: Insufficient numeric input validation **Risk Level**: High ### Vulnerable Code ```python def calculate_cost(self, provider: str, model: str, input_tokens: int, output_tokens: int) -> float: """Calculate cost for a usage event.""" # Look up pricing key = f"{provider}/{model}" if key not in self.pricing: # Try provider wildcard key = f"{provider}/*" if key not in self.pricing: # Unknown model, return 0 and warn print(f"Warning: No pricing data for {provider}/{model}", file=sys.stderr) return 0.0 pricing = self.pricing[key] # Calculate cost (pricing is per million tokens) input_cost = (input_tokens / 1_000_000) * pricing['input'] output_cost = (output_tokens / 1_000_000) * pricing['output'] return input_cost + output_cost ``` ```python def log_usage(self, provider: str, model: str, input_tokens: int, output_tokens: int, task: Optional[str] = None, session: Optional[str] = None, metadata: Optional[Dict] = None) -> Tuple[float, str]: """Log API usage and return cost + status.""" # Calculate cost cost = self.calculator.calculate_cost(provider, model, input_tokens, output_tokens) # Create record record = UsageRecord(provider, model, input_tokens, output_tokens, cost, task, session, metadata) # Check budget before logging daily_cost = self.get_daily_cost() status, message = self.budget_tracker.check_budget('daily', daily_cost + cost) ``` ```python parser.add_argument('--input-tokens', type=int, help='Input tokens') parser.add_argument('--output-tokens', type=int, help='Output tokens') ``` ### Technical Analysis The CLI and public `log_usage()` API accept arbitrary integers without checking that token counts are non-negative. `calculate_cost()` directly multiplies thes ...[truncated 1611 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate token counts at every public entry point before calculating or storing costs. - Require `input_tokens` and `output_tokens` to be non-negative integers. Consider setting a reasonable configurable upper bound. - Reject booleans explicitly when validating programmatic input because Python treats `bool` as a subclass of `int`. - Validate configured prices as finite, non-negative numeric values. - Validate `estimated_cost` in `check_before_call()` as finite and non-negative. - Do not silently normalize invalid values to zero; raise a clear exception and do not alter the ledger. - Add regression tests for negative values, excessively large values, booleans, non-finite estimates, and malformed pricing. Example validation: ```python def validate_token_count(name: str, value: int) -> None: if isinstance(value, bool) or not isinstance(value, int) or value < 0: raise ValueError(f"{name} must be a non-negative integer") validate_token_count("input_tokens", input_tokens) validate_token_count("output_tokens", output_tokens) ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
token_tamer.py:146
Finding
Usage Ledger Failures Are Silently Treated as Successful Accounting<![CDATA[ ## Vulnerability Details **File Location**: `token_tamer.py:146-170`, `token_tamer.py:218-244` **Vulnerability Type**: Fail-open storage and accounting behavior **Risk Level**: High ### Vulnerable Code ```python def load(self): """Load records from disk.""" if self.storage_file.exists(): try: with open(self.storage_file, 'r') as f: data = json.load(f) self.records = [UsageRecord.from_dict(r) for r in data.get('records', [])] except Exception as e: print(f"Error loading usage data: {e}", file=sys.stderr) self.records = [] def save(self): """Save records to disk.""" try: self.storage_file.parent.mkdir(parents=True, exist_ok=True) with open(self.storage_file, 'w') as f: data = { 'last_updated': datetime.now().isoformat(), 'records': [r.to_dict() for r in self.records] } json.dump(data, f, indent=2) except Exception as e: print(f"Error saving usage data: {e}", file=sys.stderr) ``` ```python # Log the record self.storage.add_record(record) # Check throttle if self.budget_tracker.should_throttle('daily', daily_cost + cost): print(f"⚠️ THROTTLE WARNING: {message}", file=sys.stderr) return cost, 'THROTTLE' if message: print(f"💰 Budget status: {message}", file=sys.stderr) return cost, status ``` ### Technical Analysis Ledger loading and saving catch every exception, print an error, and continue. A malformed or partially written JSON file is therefore interpreted as an empty spending history. A failed save is also hidden from `add_record()` and `log_usage()`, which can report a normal status even though the record was not durably persisted. The storage implementation overwrites the live JSON file directly and provides no file locking or atomic replacement. The project documentation acknowledges that concurrent writers can corrupt the file. Once corr ...[truncated 1648 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Fail closed when the ledger cannot be loaded or durably written. Budget checks should not proceed using an assumed zero balance. - Propagate storage exceptions to callers instead of only printing them. - Preserve malformed files for recovery and require explicit administrative action before resetting accounting state. - Write JSON to a temporary file in the same directory, flush it, call `os.fsync()`, and atomically replace the live ledger with `os.replace()`. - Apply restrictive file permissions appropriate to the platform, such as owner-only access where feasible. - Introduce inter-process locking around the complete read-modify-write transaction. - Consider SQLite with transactions and locking instead of a whole-file JSON store. - Make `add_record()` return only after durable persistence succeeds. - Add tests for malformed JSON, truncated files, permission failures, full disks, interrupted writes, and multiple concurrent writers. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
token_tamer.py:218
Finding
Threshold-Crossing Usage Is Not Recorded and Only the Daily Budget Is Enforced<![CDATA[ ## Vulnerability Details **File Location**: `token_tamer.py:218-244`, `token_tamer.py:302-315` **Vulnerability Type**: Incomplete and inconsistent budget enforcement **Risk Level**: High ### Vulnerable Code ```python def log_usage(self, provider: str, model: str, input_tokens: int, output_tokens: int, task: Optional[str] = None, session: Optional[str] = None, metadata: Optional[Dict] = None) -> Tuple[float, str]: """Log API usage and return cost + status.""" # Calculate cost cost = self.calculator.calculate_cost(provider, model, input_tokens, output_tokens) # Create record record = UsageRecord(provider, model, input_tokens, output_tokens, cost, task, session, metadata) # Check budget before logging daily_cost = self.get_daily_cost() status, message = self.budget_tracker.check_budget('daily', daily_cost + cost) # Check kill switch if self.budget_tracker.should_kill('daily', daily_cost + cost): self.kill_switch_active = True print(f"🚨 KILL SWITCH ACTIVATED: {message}", file=sys.stderr) return cost, 'KILL' # Log the record self.storage.add_record(record) ``` ```python def check_before_call(self, estimated_cost: float = 0.10) -> bool: """Check if API call should proceed (return True = OK, False = blocked).""" if self.kill_switch_active: print("🚨 KILL SWITCH ACTIVE: API calls blocked", file=sys.stderr) return False daily = self.get_daily_cost() if self.budget_tracker.should_kill('daily', daily + estimated_cost): self.kill_switch_active = True print("🚨 KILL SWITCH ACTIVATED: Budget exceeded", file=sys.stderr) return False return True ``` ### Technical Analysis `log_usage()` is documented as being called after an API request. At that point the provider charge has already occurred. However, when that completed request takes the daily total to or above 100%, the function returns `KILL` b ...[truncated 1851 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Always persist completed usage before returning a post-call budget status, including when the record causes a threshold to be exceeded. - Distinguish pre-call authorization from post-call accounting. A post-call logger must never discard a charge that has already occurred. - Evaluate daily, weekly, and monthly projected totals in `check_before_call()`. - Evaluate all configured periods after recording completed usage. - Persist enforcement state or derive it from the complete ledger on every startup. - Treat the strictest applicable period as authoritative: if any configured period is exhausted, block the call. - Respect configuration options such as `AUTO_KILL_SWITCH`, or remove them if unsupported. - Document that estimated pre-call costs can differ from final costs and require final usage reconciliation. - Add tests covering threshold-crossing calls, process restart after exhaustion, and independently exhausted daily, weekly, and monthly budgets. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code focuses narrowly on report generation from previously stored usage data. It aggregates call counts, token counts, and costs across time periods and categories and outputs summaries. While this supports the 'monitor' aspect of the description, it does not implement budget enforcement, waste detection, or optimization recommendation logic. It also does not itself track API calls; it relies on an external UsageStore. Therefore, the declared description overstates the capabilities of this specific code chunk, making it a mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill documents Python code that reads configuration, writes usage data, and may interact with external provider contexts, but it does not declare any tool scope such as allowed-tools or permissions. In an agent ecosystem, undeclared file and network capabilities reduce reviewability and can let a seemingly simple budgeting skill access local files or make outbound requests without explicit operator approval.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code persistently writes usage records to a local JSON file, including task, session, and arbitrary metadata, without any minimization, consent, redaction, or protection. In a cost-tracking skill, those fields can easily contain prompts, identifiers, customer data, API-related context, or other sensitive operational information, creating a privacy and data exposure risk if the file is read by other users, backed up, or exfiltrated.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The natural-language policy check applies to all file types and includes locale-related constraints. Lines L073-L081 explicitly state 'Token Tamer assumes USD' and 'USD only' with no opt-in or documented regional justification, which can be interpreted as forcing a locale-specific convention.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The README explicitly advertises persistent JSON-based storage of API usage data but does not warn that the stored metadata may include sensitive operational details such as model usage, task names, session identifiers, and spending patterns. In a cost-tracking skill, this omission can lead users to retain potentially sensitive telemetry on disk without considering access controls, retention limits, or sanitization.

Scope Creep

Low
Category
Excessive Agency
Content
**USE AT YOUR OWN RISK.**

- The author(s) are NOT liable for any damages, losses, or consequences arising from 
  the use or misuse of this software — including but not limited to financial loss, 
  data loss, security breaches, business interruption, or any indirect/consequential damages.
- This software does NOT constitute financial, legal, trading, or professional advice.
- Users are solely responsible for evaluating whether this software is suitable for
Confidence
70% confidence
Finding
Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Missing User Warnings

Low
Confidence
81% confidence
Finding
This code prints detailed usage analytics, including task names and provider/model breakdowns, directly to stdout in text or JSON form. While reporting is the skill's purpose, there is no disclosure in this file that the generated output may contain sensitive operational metadata, which matters because the tool supports machine-readable JSON export and broad date-range reporting.

Static analysis

No suspicious patterns detected.