Back to skill

Security audit

Nervtimer

Security checks for vulnerabilities and agentic risk

Overview

This timer skill matches its reminder purpose, but it schedules persistent agent turns that are told to run shell commands built from unvalidated timer data.

Review before installing. The skill is not proven malicious, but it should validate timer IDs, encode reminder title and reason as inert data, avoid natural-language shell commands in scheduled agent turns, constrain cron job permissions, and make reminder language user-configurable. Users should understand it creates persistent reminders and local state until timers are completed or canceled.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/build-cron-payload.sh:49
Finding
Stored Prompt Injection Through User-Controlled Reminder Content## Vulnerability Details **File Location**: `scripts/build-cron-payload.sh:49-62` **Vulnerability Type**: Stored instruction injection into a scheduled Agent turn **Risk Level**: High ### Vulnerable Code ```bash payload: { kind: "agentTurn", message: ( "NERVTIMER ARM for timer_id=" + $t.timer_id + ". " + "Run deterministic state transition with: " + "bash " + $base_dir + "/scripts/state.sh start-nagging " + $t.timer_id + ". " + "Then generate one short reminder in the assistant personality. " + "Task: " + $t.title + (if ($t.reason // "") != "" then ". Reason: " + $t.reason else "" end) + "." ) }, delivery: { mode: "announce" } ``` The corresponding validation at `scripts/validate-intent.sh:20-21` only establishes that the title is a nonempty string: ```bash ($root.timer.title | is_non_empty_string) and (($root.timer.schedule_type == "one_shot") or ($root.timer.schedule_type == "recurrent")) ``` ### Technical Analysis The timer's `title`, `reason`, and `timer_id` originate from structured user intent. These values are concatenated directly into the `message` of an `agentTurn`. No unambiguous data delimiters, content-length restrictions, control-character restrictions, or instructions requiring the scheduled Agent to treat these fields exclusively as inert data are applied. Because an `agentTurn` message is interpreted as instructions rather than as a typed data structure, malicious text in a title or reason can be treated as additional instructions. This creates a stored prompt-injection condition: the attacker-controlled content is first incorporated into the cron payload and then interpreted later when the scheduled job runs. ### Attack Path 1. An attacker asks the skill to create a timer with a title or reason containing adversarial instructions, for example text directing the future Agent to ignore the reminder workflow and invoke a ...[truncated 1165 chars]
Remediation
## Remediation Suggestions 1. Keep the scheduled Agent instruction static and pass timer properties through a structured data mechanism rather than interpolating them into prose instructions. 2. Clearly delimit all untrusted fields and state explicitly that their contents are data that must never be interpreted as instructions. 3. Encode structured timer data, such as with JSON, and require the scheduled handler to parse only the expected fields. 4. Enforce maximum lengths and reject control characters or other content that is unnecessary for reminder titles and reasons. 5. Use deterministic code to compose the final reminder where possible, reducing exposure of untrusted content to an instruction-following model. 6. Restrict scheduled Agent sessions to the minimum required tools and permissions so that prompt injection cannot produce unrelated privileged actions. 7. Add tests using titles and reasons containing instruction-like text and verify that the content is reproduced only as reminder data.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/build-cron-payload.sh:54
Finding
Shell Argument Injection in Generated Scheduled Agent Commands## Vulnerability Details **File Location**: `scripts/build-cron-payload.sh:54-56` and `scripts/build-cron-payload.sh:70-72` **Vulnerability Type**: Unquoted user-controlled value embedded in shell command text **Risk Level**: High ### Vulnerable Code Arm-job command construction: ```bash "NERVTIMER ARM for timer_id=" + $t.timer_id + ". " + "Run deterministic state transition with: " + "bash " + $base_dir + "/scripts/state.sh start-nagging " + $t.timer_id + ". " + ``` Nag-job command construction: ```bash "NERVTIMER NAG TICK for timer_id=" + $t.timer_id + ". " + "Call bash " + $base_dir + "/scripts/state.sh next-nag " + $t.timer_id + " and parse JSON result. " + "If should_nag=false, do not send a reminder. " + ``` The base directory is also environment-overridable at `scripts/build-cron-payload.sh:15-16`: ```bash BASE_DIR_DEFAULT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" BASE_DIR="${NERVTIMER_BASE_DIR:-$BASE_DIR_DEFAULT}" ``` ### Technical Analysis The generated Agent messages contain apparent shell commands assembled by direct string concatenation. The timer identifier is appended as a shell argument without quoting or character validation. The command path derived from `NERVTIMER_BASE_DIR` is likewise inserted without shell-safe quoting. `validate-intent.sh` does not validate `timer.timer_id`, and `state.sh` only checks that an identifier exists during upsert. Consequently, a timer identifier can contain whitespace, shell metacharacters, command substitutions, separators, or redirections. The generated text is not itself executed directly by `build-cron-payload.sh`; exploitation depends on the scheduled Agent following the message and executing the displayed command through a shell. The project workflow explicitly instructs the Agent to run these commands, making that execution path credible. ### Attack Path 1. An attacker supplies a crafted `timer_id` containing shell syntax, such as ...[truncated 1445 chars]
Remediation
## Remediation Suggestions 1. Require timer identifiers to match a strict allowlist, such as `^[A-Za-z0-9_-]{1,64}$`, in both `validate-intent.sh` and `state.sh`. 2. Do not communicate executable shell commands through natural-language Agent messages. Invoke the state script through a structured execution interface with a fixed executable and separate argument array. 3. If shell command generation is unavoidable, quote every argument with a proven shell-escaping mechanism rather than manual concatenation. 4. Resolve the base directory to a canonical trusted installation path and reject environment overrides outside that path. 5. Quote the script path independently from its arguments and reject paths containing unexpected control characters. 6. Apply validation again at the execution boundary because payloads or stored timer records may bypass initial intent validation. 7. Run scheduled handlers with least privilege and expose only the state-transition operation required by the job. 8. Add regression tests using identifiers containing spaces, semicolons, command substitutions, pipes, redirections, quotes, and newline characters, and confirm that all are rejected.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description is broader than the supplied code. The code does match one portion of the description: it maintains deterministic local state for a nagging lifecycle and escalates a tone stage over time. However, the major promised behaviors are absent from this chunk: there is no cron usage, no scheduling or trigger execution, no recurring timer support, no channel delivery, and no reminder text generation at all. The code is therefore a state backend for such a skill rather than the full described timer/nagging system.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The code does not implement the described timer/reminder behavior; it only validates incoming JSON structure for timer intents. While the validated actions relate to timers, the primary purpose is input validation rather than scheduling, reminder delivery, stateful nagging, or message generation. The description claims substantial capabilities—cron-based scheduling, deterministic local state, repeated reminders until completion, and LLM-generated escalating text—that are absent from this code chunk. Additionally, the code accepts an every_ms recurrence mode, which is not reflected in the declared description.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The activation guidance says to use the skill when the user wants a reminder timer, but provides no strong exclusions or tighter trigger boundaries. In agent environments, broad activation criteria can cause the skill to activate on loosely related conversation, leading to unintended scheduling actions, cross-channel reminders, or state changes the user did not clearly request.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The completion trigger examples include generic phrases like 'done' that frequently appear in ordinary conversation. Because this skill stops nagging only on explicit completion, ambiguous trigger handling can let unrelated messages accidentally mark timers complete, causing missed reminders and undermining the reliability of the reminder system.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script constructs scheduler payloads that instruct a downstream agent to execute a shell command containing untrusted timer data, including timer_id and a filesystem base path. Even though this file does not itself execute the command, it serializes operational shell instructions into an automated job message, creating an indirect command-execution path and normalizing unsafe agent behavior without validation or user warning.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This is a second automated shell-invocation path: the nag job message tells an agent to run a bash command and parse its JSON output during recurring execution. Because the job runs every 5 minutes and is designed to keep acting until stopped, any unsafe command interpretation or data injection in the surrounding system can be repeatedly triggered, increasing blast radius and persistence.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The natural-language instruction says to "send exactly one short reminder in German," which forces a specific language. The file does not indicate user opt-in, configurability, or a documented region-specific justification, so this appears to violate the language/locale policy.

Static analysis

No suspicious patterns detected.