Back to skill

Security audit

Unformal Notifications

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with Unformal notifications, but it asks users to install a mutable remote script, persist an API key, and store response data locally in ways that need careful review.

Review before installing. Prefer using the bundled listener script instead of downloading a mutable remote script, do not store the API key in ~/.zshrc, use a secret manager or restricted env file, set restrictive permissions on ~/.unformal, and avoid running the listener with sensitive macOS automation permissions unless you trust the response source and service.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:98
Finding

Unverified Remote Script Retrieval and Execution

Content
View full analysis
~/bin/unformal-listen chmod +x ~/bin/unformal-listen export UNFORMAL_API_KEY=unf_xxx # add to ~/.zshrc for persistence ``` The downloaded file is subsequently intended to be run as follows: ```bash unformal-listen # lists your Pulses unformal-listen # starts listening ``` ### Technical Analysis The installation instructions retrieve a mutable shell script from an external URL, mark it executable, and direct the user to run it. There is no version pin, cryptographic checksum, signature verification, or comparison against the listener already included in the Skill package. Consequently, the effective code executed by the user is not necessarily the code reviewed in `scripts/unformal-listen.sh`. The external resource can change after publication or audit. Although HTTPS provides transport protection, it does not protect against compromise of the hosting account, deployment pipeline, origin server, or authorized content being replaced. Fetching executable code is not required at this privilege level because the project already contains a local copy at `scripts/unformal-listen.sh`. ### Attack Path 1. An attacker compromises the external script resource, website deployment process, or an account authorized to update the resource. 2. The attacker replaces `https://unformal.ai/unformal-listen.sh` with a malicious shell payload. 3. A user follows the documented installation procedure. 4. `curl` writes the attacker-controlled content to `~/bin/unformal-listen`. 5. The file is marked executable without integrity verification. 6. The user invokes `unformal-listen` while an Unformal API key is available in the environment. 7. The payload executes with the user's ...[truncated 642 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:45
Finding

Plaintext Persistence of the Unformal API Key

Content
View full analysis
/dev/null || echo 0)` 3. Fetch new responses: ```bash TMP=$(mktemp) curl -fsS "https://unformal.ai/api/v1/pulses//conversations?completedSince=$SINCE" \ -H "Authorization: Bearer " > "$TMP" ``` ``` Additional plaintext persistence is recommended at `SKILL.md:101`: ```bash export UNFORMAL_API_KEY=unf_xxx # add to ~/.zshrc for persistence ``` ### Technical Analysis The Skill explicitly permits embedding the API key inline in a scheduled Skill file and recommends adding it to a general-purpose shell startup file. Both approaches persist the credential as plaintext. A scheduled task definition may be read by agents or processes that inspect Claude task files. A shell profile is loaded into many interactive shell sessions, unnecessarily expanding the set of processes that receive the credential. These files may also be included in backups, support bundles, synchronization systems, or accidental repository commits. The API requires authentication, but permanent plaintext storage in broadly used configuration files is not necessary for that functionality. ### Attack Path 1. A user follows the setup instructions and embeds the key in a scheduled `SKILL.md` or adds it to `~/.zshrc`. 2. The key remains stored as plaintext across sessions. 3. A local application, agent, backup reader, malicious script, or another process running under the user account reads the file. 4. The attacker extracts the bearer token. 5. The attacker submits authenticated requests to the Unformal API and accesses resources permitted by that token. ### Impact Assessment The exposed tok ...[truncated 489 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/unformal-listen.sh:75
Finding

AppleScript Injection Through Remotely Influenced Notification Content

Content
View full analysis
/dev/null 2>&1; then # macOS osascript -e "display notification \"${msg//\"/\\\"}\" with title \"${title//\"/\\\"}\" sound name \"Glass\"" 2>/dev/null || true elif command -v notify-send >/dev/null 2>&1; then # Linux notify-send "$title" "$msg" || true fi } ``` The interpolated message is derived from a server-provided SSE event at `scripts/unformal-listen.sh:118-131`: ```bash summary=$(printf '%s' "$data" | python3 -c ' import sys, json try: d = json.load(sys.stdin) echo = d.get("echo") or {} s = echo.get("summary") or d.get("summary") or "New response received" sys.stdout.write(s[:180]) except: sys.stdout.write("New response received") ' 2>/dev/null || echo "New response received") echo "✨ $(date +%H:%M:%S) $summary" NOTIFY_FN "Unformal — new response" "$summary" ``` ### Technical Analysis The listener constructs AppleScript source code by interpolating a response summary into the argument passed to `osascript -e`. It attempts to escape double quotes, but quote replacement alone is not a complete AppleScript string encoder. In particular, backslashes and control characters are not handled as part of a defined, context-safe encoding procedure. The message originates from an SSE event and may be influenced by Pulse response content or server-side summary generation. Data crossing that trust boundary must not be incorporated directly into executable source code. The secure design is to keep the AppleScript program static and pass the title and message as positional arguments. This separates data from code and removes the need to construct AppleScript source dynamically. ### Attack Path 1. An attacker submits crafted content t ...[truncated 1425 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/unformal-listen.sh:43
Finding

Sensitive Pulse Responses Stored Without Explicit Restrictive Permissions

Content
View full analysis
"$file" ``` The documented event shape in `SKILL.md` shows that these files can contain sensitive business and respondent information, including budget, timeline, summaries, quotations, sentiment, and metadata. ### Technical Analysis The script relies entirely on the caller's current `umask` when creating `~/.unformal`, the inbox directory, and response files. Under permissive configurations, other local users may receive read or traversal access. The script neither sets `umask 077` nor explicitly applies directory mode `700` and file mode `600`. The timestamp filename has only one-second precision. If multiple completion events are processed in the same second, they resolve to the same path and a later event can overwrite an earlier response. This is primarily an integrity and availability concern, while the missing permission controls are a confidentiality concern. Persisting response data is part of the declared inbox functionality, but access should be limited to the owning user. ### Attack Path 1. The listener is launched in an environment with a permissive `umask`. 2. It creates `~/.unformal/inbox` and response JSON files without explicit restrictive modes. 3. Another local account or process with filesystem access enumerates or reads the files. 4. Confidential response content is disclosed. For the collision condition: 1. Two `conversation.completed` events arrive within the same second. 2. Both eve ...[truncated 638 chars]
Remediation
View remediation
"$file" chmod 600 "$file" ``` - If stable names are needed, include nanoseconds, the conversation ID, or a securely generated random suffix. - Validate that existing directories are owned by the current user and are not symbolic links before writing. - Define a retention policy and securely remove or encrypt old response files. - Document that inbox and processed-response directories contain confidential data and must not be placed in shared or synchronized locations without user approval. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 44)May include surrounding context.

md
Creates a directory at `~/.claude/scheduled-tasks/<routine-name>/` containing a single `SKILL.md` file with:

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The one-time install instructions download and execute a remote shell script with no explicit safety warning, provenance validation, or integrity checks. Because the script is then run locally and paired with a persistent API credential, compromise of the hosting site or script contents would enable arbitrary code execution and secret theft.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger example "did anyone finish the survey?" is generic and could match ordinary conversation outside the Unformal-specific context. The file does not provide exclusion conditions or clarifying constraints to limit activation to this skill.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 40)May include surrounding context.

md
Ask Claude:

> **"Create a Claude Code desktop routine called `unformal-new-responses` that checks for new completed responses on my Unformal Pulse `<PULSE_ID>` using API key `<API_KEY>`. It should track the last-seen timestamp in `~/.unformal/last-seen`, only fetch new responses since that marker, and give me a concise digest (sentiment + summary + key quotes) or say 'No new responses' if there are none."**

### What Claude does

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

Installing a long-lived executable under ~/bin and advising persistent API key configuration creates recurring local execution and credential persistence. In combination with the remote script download, this materially increases blast radius because a compromised listener could continue running with durable access to incoming response data and secrets.

Content

Scanner excerpt · SKILL.md (reported line 107)May include surrounding context.

One-time install

bash
mkdir -p ~/bin
curl -fsS https://unformal.ai/unformal-listen.sh > ~/bin/unformal-listen
chmod +x ~/bin/unformal-listen
export UNFORMAL_API_KEY=unf_xxx   # add to ~/.zshrc for persistence

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs the user to download a shell script from a remote domain into ~/bin and execute it without any integrity verification, code review step, pinning, or checksum validation. That creates a direct supply-chain and arbitrary code execution risk on the local machine, which is especially dangerous because the script is granted access to the user's environment and API key.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The skill recommends placing the Unformal API key in the shell environment and persisting it via ~/.zshrc, which increases the exposure window of the credential beyond the immediate task. Persistent credentials in common shell startup files are easy to leak through backups, dotfile syncing, local read access, debugging output, or accidental sharing.

Content

Scanner excerpt · SKILL.md (reported line 110)May include surrounding context.

mkdir -p ~/bin curl -fsS https://unformal.ai/unformal-listen.sh > ~/bin/unformal-listen chmod +x ~/bin/unformal-listen export UNFORMAL_API_KEY=unf_xxx # add to ~/.zshrc for persistence

text

### Run

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill states that event JSON and response data are written to ~/.unformal/inbox but does not prominently warn that potentially sensitive respondent content, summaries, quotes, and metadata will persist on disk. This can create unexpected local data retention and privacy exposure, especially on shared machines, synced home directories, or systems with weak file permissions.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 142)May include surrounding context.

For "since last check" semantics with a local marker file:

bash
mkdir -p ~/.unformal
LAST=$(cat ~/.unformal/last-seen 2>/dev/null || echo 0)
curl -fsS "https://unformal.ai/api/v1/pulses/<PULSE_ID>/conversations?completedSince=$LAST" \
  -H "Authorization: Bearer $UNFORMAL_API_KEY"

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 175)May include surrounding context.

After acting on events, archive

bash
mkdir -p ~/.unformal/processed
mv ~/.unformal/inbox/*.json ~/.unformal/processed/ 2>/dev/null || true

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest frames this skill as notifying users about new Unformal responses and checking their inbox, but this section expands into follow-up automation like drafting Slack messages and updating a CRM. Those actions are not necessary to implement notifications and represent extra capability outside the declared purpose.

Content

No source excerpt is available for this finding.