Back to skill

Security audit

car-maintenance-timeline

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local car-maintenance calculator with disclosed inputs and no evidence of hidden network access, persistence, credential use, or destructive behavior.

Reasonable to install if you want a local advisory maintenance calculator. Treat its schedule as generic, verify against your owner's manual and local inspection rules, and avoid feeding untrusted malformed history JSON until the input-validation bug is 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 (1)

T09 · Insecure Skill Coding Practices

Note
Location
scripts/car_maintenance.py:253
Finding

Malformed Service History Entry Causes an Unhandled Exception

Content
View full analysis

Vulnerability Details

File Location: scripts/car_maintenance.py, lines 253–258
Vulnerability Type: Improper input validation leading to denial of service
Risk Level: Low

Vulnerable Code

python
if not isinstance(entries, list):
    raise ValueError("--history must be a JSON list of {task, km, date} objects")

resolved: dict[str, dict[str, Any]] = {}
for entry in entries:
    raw_id = str(entry.get("task", "")).strip()

Technical Analysis

The resolve_history function verifies that the decoded top-level JSON value is a list, but it does not verify that each member of that list is an object.

Consequently, valid JSON inputs such as [null], [1], ["value"], or [[]] pass the top-level validation. The code then invokes entry.get(...) on a value that does not implement the dictionary get method, causing an AttributeError.

The exception is not converted into a controlled validation error. The main function only catches ValueError, so the process exits unexpectedly and can expose a Python traceback to the caller.

Attack Path

  1. An attacker or malformed upstream integration controls the value passed to --history.

  2. The attacker supplies a syntactically valid JSON list containing a non-object value, for example:

    bash
    python3 scripts/car_maintenance.py status \
      --km 84500 \
      --in-service 2021-03-10 \
      --history '[null]'
    
  3. json.loads() successfully decodes the input as a list.

  4. The top-level isinstance(entries, list) check succeeds.

  5. The loop assigns None to entry.

  6. entry.get("task", "") raises an unhandled AttributeError.

  7. The command terminates without producing the requested maintenance report.

Impact Assessment

Exploitation causes denial of service for the affected local CLI invocation. This can interrupt an automated Agent workflow that forwards untrusted or malformed service-history data t ...[truncated 237 chars]

Remediation
View remediation

Remediation Suggestions

Validate every history-list member before accessing dictionary methods. Reject invalid entries with a controlled ValueError that identifies the offending array index:

python
resolved: dict[str, dict[str, Any]] = {}
for index, entry in enumerate(entries):
    if not isinstance(entry, dict):
        raise ValueError(
            f"--history entry at index {index} must be an object "
            "containing task, km, and date fields"
        )

    raw_id = str(entry.get("task", "")).strip()

Additional hardening should include:

  1. Validate that task is a non-empty string.
  2. Validate km as a non-negative integer and reject booleans, negative values, and unreasonable magnitudes.
  3. Validate that history dates are not before the in-service date or after the effective current date, where appropriate.
  4. Convert expected input-conversion failures into controlled ValueError messages without tracebacks.
  5. Add regression tests for [null], [1], ["value"], [[]], and mixed valid/invalid arrays.
  6. Verify that all malformed cases return exit code 2 and a concise error on standard error.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • 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
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill instructs the agent to run local shell commands (python3 scripts/car_maintenance.py ...) but does not declare any explicit tool scope such as permissions or allowed-tools. That mismatch can cause the runtime to grant broader-than-intended execution capability or make review and policy enforcement harder, which is risky even though the demonstrated commands are benign car-maintenance computations.

Content

No source excerpt is available for this finding.

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/car_maintenance.py (reported line 410)May include surrounding context.

python
done_before = task["id"] in done
        last_km = rec["km"] if rec else 0
        last_date = rec["date"] if rec else in_service
        for _ in range(200):  # hard cap: never loop forever
            ev = evaluate_task(task, last_km, last_date, current_km, today,
                               annual_km, severe, done_before)
            nxt = ev["next"]

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/test_car_maintenance.py (reported line 14)May include surrounding context.

python
SCRIPT = Path(__file__).parent / "car_maintenance.py"

spec = __import__("importlib.util").util.spec_from_file_location(
    "car_maintenance", SCRIPT)
cm = __import__("importlib.util").util.module_from_spec(spec)
spec.loader.exec_module(cm)

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/test_car_maintenance.py (reported line 16)May include surrounding context.

python
spec = __import__("importlib.util").util.spec_from_file_location(
    "car_maintenance", SCRIPT)
cm = __import__("importlib.util").util.module_from_spec(spec)
spec.loader.exec_module(cm)

PASS = 0

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_car_maintenance.py (reported line 34)May include surrounding context.

python
def run(args, expect_rc=0):
    proc = subprocess.run(
        [sys.executable, str(SCRIPT)] + args,
        capture_output=True, text=True,
    )

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/test_car_maintenance.py:17