Back to skill

Security audit

research-gif-enricher

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its note-enrichment purpose, but it batch-edits Bear notes and sends title-derived keywords to an external GIF service without enough user control or disclosure.

Review before installing. This skill can read and rewrite Bear notes tagged 「待整理」, may overwrite note content while removing the tag, and may send title-derived search terms to Tenor for GIF lookup. Use only after backing up notes, confirming the tagged set is safe to process, and accepting the external GIF-search data flow; a pinned grizzly dependency and opt-in network mode would reduce risk.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/process_tagged.sh:90
Finding
Hard-Coded Tenor API Credential<![CDATA[ ## Vulnerability Details **File Location**: `scripts/process_tagged.sh:90-95` **Vulnerability Type**: Hard-coded API credential **Risk Level**: Medium ### Vulnerable Code ```bash GIF_URL=$(curl -s "https://tenor.googleapis.com/v2/search?q=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")&key=AIzaSyAyimkuYQYF_FXVALexPuGQctUWRURdCYQ&limit=1" 2>/dev/null | python3 -c " import sys, json data = json.load(sys.stdin) results = data.get('results', []) if results: print(results[0].get('media_formats', {}).get('gif', {}).get('url', '')) " 2>/dev/null || echo "") ``` ### Technical Analysis The script embeds a Google/Tenor API key directly in distributed source code. Anyone with access to the package can extract and reuse this credential independently of the Skill. Client-distributed API keys should be treated as publicly exposed unless they are tightly restricted at the provider. The available source does not establish that this key has API, referrer, application, billing, or quota restrictions. Although the key is not a Bear token and does not directly grant access to local notes, its exposure creates credential-abuse and operational risks. ### Attack Path 1. An attacker downloads or inspects the Skill package. 2. The attacker extracts the API key from line 90. 3. The attacker submits unrelated requests to compatible Google APIs using that key. 4. If provider-side restrictions are insufficient, the requests consume the owner's quota or generate charges and abuse attributed to the key owner. 5. Sustained misuse may cause service degradation or revocation of the key, breaking GIF lookup for legitimate users. ### Impact Assessment The exposed key does not provide local shell privileges or access to the Bear token based on the audited code. Its scope is limited to permissions granted to the Google API project. Potential consequences include unauthorized API usage, quota exhaustion, billing impact, service interruptio ...[truncated 69 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed API key immediately. 2. Remove the credential from source code and repository history. 3. Read the credential from a protected environment variable or secret manager, for example: ```bash : "${TENOR_API_KEY:?TENOR_API_KEY must be configured}" ``` 4. Restrict the replacement key to the minimum required Tenor API, with conservative quotas and billing alerts. 5. Apply every provider-supported application, source, IP, or referrer restriction compatible with the deployment model. 6. Avoid printing the key in logs or command diagnostics. 7. Fail safely with a clear configuration error when no key is supplied. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:14
Finding
Unpinned Installation of a Mutable Executable Dependency<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:14-18` **Vulnerability Type**: Mutable third-party dependency **Risk Level**: Medium ### Vulnerable Code ```yaml install: - id: go kind: go module: github.com/tylerwince/grizzly/cmd/grizzly@latest bins: [grizzly] label: Install grizzly (go) ``` ### Technical Analysis The installation metadata requests `grizzly@latest`. The meaning of `latest` can change after this Skill has been reviewed, so future installations need not retrieve the same source code or binary behavior that was originally audited. Because `grizzly` is subsequently invoked with access to the Bear token file and note operations, compromise of the upstream repository, release process, module resolution path, or maintainer account could introduce malicious code into a trusted workflow. The audit found no evidence that the current upstream dependency is malicious; the issue is the absence of an immutable, reviewed version boundary. ### Attack Path 1. A user or automated installer processes the Skill metadata. 2. Go resolves `github.com/tylerwince/grizzly/cmd/grizzly@latest` at installation time. 3. An upstream compromise or malicious future release changes the code selected by `latest`. 4. The compromised program is installed under the legitimate `grizzly` name. 5. When the batch script invokes it, the program can act with the invoking user's privileges and receive the Bear token-file path and note data involved in commands. ### Impact Assessment A compromised dependency would execute with the privileges of the user installing or running it. Within this workflow, it could potentially read files accessible to that user, access the Bear token, inspect or alter Bear notes, send data over the network, and execute additional local actions. No privilege escalation beyond the invoking account is demonstrated, but the potential scope is broader than the Skill's intended note-enrichment function. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `@latest` with a reviewed, immutable release version or commit: ```yaml module: github.com/tylerwince/grizzly/cmd/grizzly@vX.Y.Z ``` 2. Record and verify the expected module checksum through Go's checksum facilities. 3. Review the pinned release and its transitive dependencies before distribution. 4. Use automated dependency monitoring to propose explicit, separately reviewed upgrades. 5. Where practical, publish reproducible build information or verified binary hashes. 6. Document the dependency's need to access the Bear token and keep that access limited to commands that require it. ]]>

other

Warning
Location
scripts/process_tagged.sh:90
Finding
Disclosure of Bear Note Title Keywords to an External GIF Provider<![CDATA[ ## Vulnerability Details **File Location**: `scripts/process_tagged.sh:62-68, 88-95` **Vulnerability Type**: Privacy-sensitive data disclosure **Risk Level**: Medium ### Vulnerable Code ```bash # Derive search keywords from title (first 3 meaningful words) QUERY=$(echo "$TITLE" | python3 -c " import sys, re title = sys.stdin.read().strip() words = re.findall(r'[a-zA-Z\u4e00-\u9fff]+', title) print(' '.join(words[:3]) if words else title[:30]) " 2>/dev/null || echo "${TITLE:0:30}") ``` ```bash # Fallback: search via web if [ -z "$GIF_URL" ]; then GIF_URL=$(curl -s "https://tenor.googleapis.com/v2/search?q=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")&key=AIzaSyAyimkuYQYF_FXVALexPuGQctUWRURdCYQ&limit=1" 2>/dev/null | python3 -c " import sys, json data = json.load(sys.stdin) results = data.get('results', []) if results: print(results[0].get('media_formats', {}).get('gif', {}).get('url', '')) " 2>/dev/null || echo "") fi ``` ### Technical Analysis The script derives a query from up to three words in each Bear note title, or up to 30 title characters as a fallback, and transmits that query to Google Tenor over HTTPS. External search is functionally relevant to obtaining a topic-matched GIF, so the network access itself does not exceed the declared feature. However, private note titles may contain client names, project codenames, health information, research topics, or other confidential terms. The transfer is not clearly disclosed as a privacy boundary in the Skill documentation, and the script does not request confirmation, display the exact outgoing query for approval beyond routine console output, or provide a privacy-preserving local mode. The audited request does not include the Bear token or full note content. ### Attack Path 1. A user creates a private Bear note whose title contains sensitive terms and tags it `待整理`. 2. The user runs the batch-processing script. 3. The script reads the title and ...[truncated 736 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly disclose that title-derived search terms are sent to an external provider. 2. Require explicit opt-in before enabling external GIF searches. 3. Display the exact derived query and support per-note confirmation for sensitive collections. 4. Add a mode that accepts a user-supplied generic query rather than deriving one from the title. 5. Allow external fallback to be disabled, such as with `--no-network` or an environment setting. 6. Consider local classification, a curated local GIF index, or deliberate redaction of names and identifiers. 7. Document the provider and its applicable privacy policy and retention behavior. 8. Continue using TLS, and add appropriate timeouts and strict failure handling to the network request. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/process_tagged.sh:105
Finding
Stale Whole-Note Replacement Can Delete the Appended GIF and Concurrent Edits<![CDATA[ ## Vulnerability Details **File Location**: `scripts/process_tagged.sh:105-115` **Vulnerability Type**: Unsafe non-atomic data replacement **Risk Level**: Medium ### Vulnerable Code ```bash # Append GIF to note ALT_TEXT="${QUERY} gif" printf '\n## Supporting Media\n\n![%s](%s)\n' "$ALT_TEXT" "$GIF_URL" \ | grizzly add-text --id "$NOTE_ID" --mode append --token-file "$TOKEN_FILE" echo " ✅ GIF appended: $GIF_URL" # Strip the tag from note content by rewriting without #待整理 if echo "$CONTENT" | grep -q '#待整理'; then NEW_CONTENT=$(echo "$CONTENT" | sed 's/#待整理//g' | sed '/^$/N;/^\n$/d') echo "$NEW_CONTENT" | grizzly add-text --id "$NOTE_ID" --mode replace --token-file "$TOKEN_FILE" echo " 🏷️ Tag #$TAG removed" fi ``` ### Technical Analysis `CONTENT` is captured before the GIF is appended. After appending the media block, the script constructs `NEW_CONTENT` from that stale pre-append value and replaces the entire note. Consequently, the replacement can immediately delete the newly appended GIF. The same read-modify-write sequence has no revision check, lock, or fresh read. Any edit made by the user or another process between the initial read and replacement can also be silently overwritten. The broad `sed 's/#待整理//g'` operation removes every matching substring rather than performing a dedicated metadata-level tag update. ### Attack Path 1. The script reads a note and stores its original text in `CONTENT`. 2. The script appends a GIF to the current note. 3. A user or another process may also modify the note during the processing interval. 4. The script removes the tag from the stale `CONTENT` variable. 5. `grizzly add-text --mode replace` replaces the complete current note with that stale value. 6. The appended GIF and any concurrent edits are lost. This issue can occur during ordinary operation and does not require an external attacker. An actor able to induce concurrent note edits could make the resulting data loss more likely, but th ...[truncated 468 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a dedicated Bear or `grizzly` tag-removal operation instead of replacing the whole note. 2. If replacement is unavoidable, construct the final note content in memory and perform one atomic write that includes both the GIF and tag removal. 3. Re-fetch the note immediately before replacement and verify a revision identifier, timestamp, or content hash. 4. Abort and report a conflict if the note changed after the initial read. 5. Create a backup or retain the original content before any whole-note replacement. 6. Restrict tag removal to an exact Bear tag token rather than replacing every matching substring. 7. Add tests verifying that the media block remains present and concurrent modifications are never silently discarded. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The core behavior mostly matches the declared purpose: it processes Bear notes tagged 「待整理」, reads them with grizzly, finds GIFs, appends them, and removes the tag. However, the description specifically says GIFs are searched via gifgrep, while the code also performs direct external network requests to the Tenor API using curl as a fallback. That is an additional capability and resource access path not disclosed in the description. The hardcoded Tenor API key further confirms this undeclared external integration. This is a meaningful mismatch in implementation behavior/resources, even though the primary purpose remains aligned.

Missing User Warnings

High
Confidence
96% confidence
Finding
The workflow proposes destructive retagging and possible note replacement or recreation, including stripping tags from content and potentially deleting old notes, without an explicit safeguard or confirmation step. Because Bear notes are user data, these operations can cause irreversible loss of metadata, duplication, or accidental deletion if the rewrite logic is incorrect.

External Script Fetching

High
Category
Supply Chain
Content
echo "  🔎 Searching GIF for: $QUERY"

  # Search GIF via gifgrep (curl to Tenor/Giphy as fallback)
  GIF_URL=""
  if command -v gifgrep &>/dev/null; then
    GIF_URL=$(gifgrep search "$QUERY" --limit 1 --json 2>/dev/null | python3 -c "
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

External Script Fetching

High
Category
Supply Chain
Content
# Fallback: search via web
  if [ -z "$GIF_URL" ]; then
    GIF_URL=$(curl -s "https://tenor.googleapis.com/v2/search?q=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$QUERY")&key=AIzaSyAyimkuYQYF_FXVALexPuGQctUWRURdCYQ&limit=1" 2>/dev/null | python3 -c "
import sys, json
data = json.load(sys.stdin)
results = data.get('results', [])
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares capabilities that clearly require shell execution and likely network access, but it does not scope or constrain those tools via explicit permissions or allowed-tools metadata. In practice, this increases the attack surface and makes it easier for an agent to invoke broader functionality than the user may expect, especially when handling local note data and external GIF lookups.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill is designed to bulk modify Bear notes by appending media and removing a workflow tag, but it does not prominently warn the user that it will perform write operations across multiple notes. This can lead to unintended content changes, loss of organizational metadata, and difficult rollback if the enrichment is low quality or the wrong notes are targeted.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill performs a direct network call to Tenor via curl even though the described behavior emphasizes local Bear-note processing plus gifgrep-based lookup. This expands the tool's data-flow and trust boundary without clear declaration, and causes note-derived query terms to be sent to a third party. In a note-processing skill, undeclared outbound requests are risky because users may not expect their note topics to leave the device.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The script embeds a hardcoded Tenor/Google API key directly in source. Hardcoded secrets are easily extracted, reused, rotated poorly, and may enable unauthorized use of the API account or quota exhaustion. In this skill, the key also enables undeclared external access beyond the minimal local note-tidying function.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script sends note-derived search terms based on the note title to an external GIF search API without explicit warning or consent. Research note titles can contain sensitive subjects, project names, health/legal topics, or other private metadata, so even partial disclosure to a third party can violate user expectations and privacy requirements. The skill context makes this more dangerous because Bear notes are commonly personal or confidential.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script rewrites note contents by replacing the full note text after stripping the tag, and it does so automatically across all tagged notes. Bulk content replacement can unintentionally destroy formatting, race with concurrent edits, or overwrite data if parsing or text normalization is imperfect. In a research-notes workflow, silent modification of user-authored notes increases the risk of integrity loss.

Static analysis

No suspicious patterns detected.