Back to skill

Security audit

Instagram

Security checks for vulnerabilities and agentic risk

Overview

Review recommended: this Instagram skill matches its purpose, but it can print access tokens and temporarily expose selected local media through a public tunnel.

Install only for a dedicated Meta app with minimal Instagram/Facebook permissions. Keep the .env file private and out of version control, avoid running refresh commands where stdout is logged, prefer hosted media URLs for sensitive files, and review each post or comment before allowing the command to run.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_common.js:157
Finding
Refreshed Access Tokens Are Exposed in Process Output<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/_common.js:157-172` - `scripts/_common.js:194-222` - `scripts/refresh-token.js:6-16` - `scripts/refresh-facebook-token.js:3-13` **Vulnerability Type**: Sensitive credential exposure through stdout **Risk Level**: Medium ### Vulnerable Code ```js // scripts/_common.js:157-172 const newToken = data.access_token; const expiresInDays = Math.floor(data.expires_in / 86400); // Update runtime process.env.INSTAGRAM_ACCESS_TOKEN = newToken; // Persist to .env file let envContent = fs.readFileSync(envPath, "utf-8"); envContent = envContent.replace( /INSTAGRAM_ACCESS_TOKEN=.*/, `INSTAGRAM_ACCESS_TOKEN=${newToken}` ); fs.writeFileSync(envPath, envContent); log(`IG token refreshed (expires in ${expiresInDays} days)`); return { access_token: newToken, expires_in: data.expires_in, expires_in_days: expiresInDays }; ``` ```js // scripts/_common.js:194-222 const newToken = data.access_token; const expiresInDays = data.expires_in ? Math.floor(Number(data.expires_in) / 86400) : null; process.env.FACEBOOK_USER_ACCESS_TOKEN = newToken; let envContent = fs.readFileSync(envPath, "utf-8"); if (/^FACEBOOK_USER_ACCESS_TOKEN=.*/m.test(envContent)) { envContent = envContent.replace( /^FACEBOOK_USER_ACCESS_TOKEN=.*/m, `FACEBOOK_USER_ACCESS_TOKEN=${newToken}` ); } else { envContent = envContent.trimEnd() + `\nFACEBOOK_USER_ACCESS_TOKEN=${newToken}\n`; } fs.writeFileSync(envPath, envContent); if (expiresInDays != null) { log(`FB token refreshed (expires in ${expiresInDays} days)`); } else { log("FB token refreshed"); } return { access_token: newToken, expires_in: data.expires_in, expires_in_days: expiresInDays, }; ``` ```js // scripts/refresh-token.js:6-16 (async () => { try { const { named } = parseArgs(); loadEnv(named.env); const result = await refreshIgToken(); process.stdout.write(JSON.stringify(result, null, 2) + "\n"); process.exit(0); } catch (err) { ...[truncated 2576 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Never include `access_token` in a returned result or JSON command output. 2. Return only non-sensitive status metadata: ```js return { refreshed: true, expires_in: data.expires_in, expires_in_days: expiresInDays, }; ``` 3. Apply the same redaction to skipped-refresh behavior; do not return the existing token when refresh is skipped. 4. Review `SPEC.md` examples and remove sample response structures that encourage returning an `access_token` field. 5. Add an output-sanitization layer that recursively redacts keys such as `access_token`, `token`, `client_secret`, and `app_secret`. 6. Ensure the `.env` file has restrictive permissions, preferably mode `0600`, before writing credentials. 7. Configure agent, CI, and observability systems not to retain sensitive command output. 8. Rotate both tokens if existing command output may have been retained in logs or transcripts. 9. Add automated tests asserting that refresh-command stdout never contains the old or refreshed token. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/_common.js:488
Finding
Local Media Is Served Through an Unauthenticated Public Tunnel<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/_common.js:488-515` - `scripts/_common.js:560-576` - `scripts/_common.js:618-638` - `scripts/_common.js:672-708` - `scripts/_common.js:753-771` **Vulnerability Type**: Unauthenticated temporary public-file exposure **Risk Level**: Low ### Vulnerable Code ```js // scripts/_common.js:488-515 function createFileServer(fileMap) { return http.createServer((req, res) => { const fileName = decodeURIComponent(req.url.replace(/^\//, "")); const entry = fileMap.get(fileName); if (entry) { res.writeHead(200, { "Content-Type": entry.mimeType, "Content-Length": entry.data.length, }); res.end(entry.data); } else { res.writeHead(404); res.end(); } }); } async function startLocalTunnel(fileMap) { const server = createFileServer(fileMap); const port = await new Promise((resolve) => { server.listen(0, () => resolve(server.address().port)); }); log(`Local server started (port: ${port})`); log("Starting cloudflared tunnel..."); const publicUrl = await startTunnel(port); log(`Public URL: ${publicUrl}`); return { server, publicUrl }; } ``` ```js // scripts/_common.js:560-576 async function postLocalImage(filePath, caption) { const { absolutePath, mimeType } = await validateImageFile(filePath); const fileName = path.basename(absolutePath); const fileMap = new Map([ [fileName, { data: fs.readFileSync(absolutePath), mimeType }], ]); let server = null; try { const tunnel = await startLocalTunnel(fileMap); server = tunnel.server; return await postImage( `${tunnel.publicUrl}/${encodeURIComponent(fileName)}`, caption ); } finally { stopTunnel(); if (server) server.close(); log("Server and tunnel stopped"); } } ``` ```js // scripts/_common.js:672-708 async function postLocalVideo(filePath, caption, options = {}) { const { absolutePath, mimeType } = validateVideoFile(file ...[truncated 3539 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a cryptographically random capability path for every served file rather than using its basename: ```js const crypto = require("crypto"); const routeToken = crypto.randomBytes(32).toString("hex"); const publicPath = `${routeToken}/${encodeURIComponent(fileName)}`; ``` 2. Do not log the complete public tunnel URL. Log only that the tunnel started successfully. 3. Enforce a short, explicit endpoint expiration independent of the media-processing timeout. 4. Track successful retrievals and close or disable each route after the expected download completes, while accounting for legitimate range or retry requests. 5. Restrict accepted HTTP methods to `GET` and `HEAD`. 6. Add conservative request and bandwidth limits to reduce repeated unauthorized retrieval. 7. Set security-oriented headers such as `Cache-Control: no-store`. 8. Prefer private object storage with short-lived signed URLs when available. Use narrowly scoped objects, unpredictable keys, minimal expiration, and immediate deletion after publication. 9. Continue warning users that selected local files will temporarily be internet-accessible, and require explicit confirmation for sensitive media. 10. Add tests verifying that basenames alone cannot retrieve files and that routes expire and reject requests after shutdown. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (34)

Credential Access

High
Category
Privilege Escalation
Content
### 2. Configure credentials

```bash
cp .env.example .env
```

Fill in your `.env`:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
### 2. Configure credentials

```bash
cp .env.example .env
```

Fill in your `.env`:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
1. Create a **Meta App** at [developers.facebook.com](https://developers.facebook.com/) and add the **Instagram** product.
2. In the App Dashboard, find your **App ID** and **App Secret** under App Settings > Basic (used as `FACEBOOK_APP_ID` / `FACEBOOK_APP_SECRET`).
3. Generate a **short-lived access token** via the Instagram Graph API Explorer or the Token Generator in the App Dashboard.
4. Exchange it for a **long-lived access token** (valid for 60 days, auto-refreshed by this skill):
   ```
   GET https://graph.instagram.com/access_token
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
1. Create a **Meta App** at [developers.facebook.com](https://developers.facebook.com/) and add the **Instagram** product.
2. In the App Dashboard, find your **App ID** and **App Secret** under App Settings > Basic (used as `FACEBOOK_APP_ID` / `FACEBOOK_APP_SECRET`).
3. Generate a **short-lived access token** via the Instagram Graph API Explorer or the Token Generator in the App Dashboard.
4. Exchange it for a **long-lived access token** (valid for 60 days, auto-refreshed by this skill):
   ```
   GET https://graph.instagram.com/access_token
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
1. Create a **Meta App** at [developers.facebook.com](https://developers.facebook.com/) and add the **Instagram** product.
2. In the App Dashboard, find your **App ID** and **App Secret** under App Settings > Basic (used as `FACEBOOK_APP_ID` / `FACEBOOK_APP_SECRET`).
3. Generate a **short-lived access token** via the Instagram Graph API Explorer or the Token Generator in the App Dashboard.
4. Exchange it for a **long-lived access token** (valid for 60 days, auto-refreshed by this skill):
   ```
   GET https://graph.instagram.com/access_token
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
1. Create a **Meta App** at [developers.facebook.com](https://developers.facebook.com/) and add the **Instagram** product.
2. In the App Dashboard, find your **App ID** and **App Secret** under App Settings > Basic (used as `FACEBOOK_APP_ID` / `FACEBOOK_APP_SECRET`).
3. Generate a **short-lived access token** via the Instagram Graph API Explorer or the Token Generator in the App Dashboard.
4. Exchange it for a **long-lived access token** (valid for 60 days, auto-refreshed by this skill):
   ```
   GET https://graph.instagram.com/access_token
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description says this skill is for Instagram account management tasks such as viewing profiles, listing posts, publishing media, and handling comments. The supplied code chunk does none of those things. It is a utility script specifically for refreshing a Facebook token using environment configuration and command-line arguments. That is a materially different primary purpose and involves authentication maintenance for Facebook resources, which is not disclosed in the description.

Credential Access

High
Category
Privilege Escalation
Content
name: instagram-api
description: Manage an Instagram account. View profile, list posts, publish images/carousels, publish videos/Reels, and read/write comments. Use when the user requests any Instagram-related task.
allowed-tools: Bash(node scripts/*)
compatibility: Requires node (v22+), npm, and cloudflared (for local file uploads). Requires env var INSTAGRAM_ACCESS_TOKEN in a .env file. Requires internet access to graph.instagram.com.
metadata:
  version: "1.0"
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
name: instagram-api
description: Manage an Instagram account. View profile, list posts, publish images/carousels, publish videos/Reels, and read/write comments. Use when the user requests any Instagram-related task.
allowed-tools: Bash(node scripts/*)
compatibility: Requires node (v22+), npm, and cloudflared (for local file uploads). Requires env var INSTAGRAM_ACCESS_TOKEN in a .env file. Requires internet access to graph.instagram.com.
metadata:
  version: "1.0"
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
name: instagram-api
description: Manage an Instagram account. View profile, list posts, publish images/carousels, publish videos/Reels, and read/write comments. Use when the user requests any Instagram-related task.
allowed-tools: Bash(node scripts/*)
compatibility: Requires node (v22+), npm, and cloudflared (for local file uploads). Requires env var INSTAGRAM_ACCESS_TOKEN in a .env file. Requires internet access to graph.instagram.com.
metadata:
  version: "1.0"
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
name: instagram-api
description: Manage an Instagram account. View profile, list posts, publish images/carousels, publish videos/Reels, and read/write comments. Use when the user requests any Instagram-related task.
allowed-tools: Bash(node scripts/*)
compatibility: Requires node (v22+), npm, and cloudflared (for local file uploads). Requires env var INSTAGRAM_ACCESS_TOKEN in a .env file. Requires internet access to graph.instagram.com.
metadata:
  version: "1.0"
---
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Known Vulnerable Dependency: sharp==0.34.5 — 2 advisory(ies): GHSA-f88m-g3jw-g9cj (sharp inherited vulnerabilities in libvips: CVE-2026-33327, CVE-2026-33328, CVE-); GHSA-rgj7-g3m4-5g8c (sharp: Vulnerabilities in libheif: GHSA-g89c-p67h-r497 and GHSA-2jg2-4ch7-h545)

High
Category
Supply Chain
Confidence
94% confidence
Finding
The lockfile pins sharp to 0.34.5, and the static finding indicates known upstream advisories affecting bundled/native image parsing components such as libvips/libheif. In an Instagram-management skill, image and video publishing features make media handling core functionality, so vulnerable image-processing code is plausibly reachable with attacker-controlled media and could lead to crashes, denial of service, or potentially memory-corruption-based compromise depending on the affected codec path.

Known Vulnerable Dependency: sharp==0.34.5 — 2 advisory(ies): GHSA-f88m-g3jw-g9cj (sharp inherited vulnerabilities in libvips: CVE-2026-33327, CVE-2026-33328, CVE-); GHSA-rgj7-g3m4-5g8c (sharp: Vulnerabilities in libheif: GHSA-g89c-p67h-r497 and GHSA-2jg2-4ch7-h545)

High
Category
Supply Chain
Confidence
98% confidence
Finding
The package explicitly depends on sharp 0.34.5, and the static analysis reports known advisories affecting that version through bundled/native image libraries such as libvips and libheif. This is especially concerning in an Instagram skill because image and video processing commonly involves handling untrusted media, which can expose the service to crashes, denial of service, or potentially memory-corruption-style exploitation in native code.

Lp1

High
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The file performs extensive outbound network operations to Instagram/Facebook APIs and also creates an externally reachable tunnel via cloudflared. Undeclared network capability increases risk because the skill can transmit local data and credentials off-host in ways not obvious from its declared interface.

Lp1

High
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The file performs extensive outbound network operations to Instagram/Facebook APIs and also creates an externally reachable tunnel via cloudflared. Undeclared network capability increases risk because the skill can transmit local data and credentials off-host in ways not obvious from its declared interface.

Credential Access

High
Category
Privilege Escalation
Content
function loadEnv(customPath) {
  envPath = customPath
    ? path.resolve(customPath)
    : path.join(__dirname, "..", ".env");

  require("dotenv").config({ path: envPath, override: true });
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
// Update runtime
  process.env.INSTAGRAM_ACCESS_TOKEN = newToken;

  // Persist to .env file
  let envContent = fs.readFileSync(envPath, "utf-8");
  envContent = envContent.replace(
    /INSTAGRAM_ACCESS_TOKEN=.*/,
Confidence
94% confidence
Finding
This code writes refreshed access tokens back into the .env file in plaintext, increasing the chance of credential theft via local compromise, backups, logs, or accidental source control inclusion. Because these are account tokens for social platforms, exposure could enable unauthorized posting, reading comments, and account misuse.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
For local uploads, the skill serves local files over HTTP and exposes them to the public internet through a cloudflared tunnel. That behavior is materially more dangerous than ordinary Instagram management because local user content becomes publicly retrievable from a transient external URL.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
Spawning cloudflared introduces external executable execution and creates a public ingress path from the internet to a local HTTP server. In the context of an Instagram skill, this is non-obvious, expands attack surface, and can leak local media or fail open if the tunnel remains available longer than intended.

Session Persistence

Medium
Category
Rogue Agent
Content
To obtain these credentials:

1. Create a **Meta App** at [developers.facebook.com](https://developers.facebook.com/) and add the **Instagram** product.
2. In the App Dashboard, find your **App ID** and **App Secret** under App Settings > Basic (used as `FACEBOOK_APP_ID` / `FACEBOOK_APP_SECRET`).
3. Generate a **short-lived access token** via the Instagram Graph API Explorer or the Token Generator in the App Dashboard.
4. Exchange it for a **long-lived access token** (valid for 60 days, auto-refreshed by this skill):
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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Ubuntu / Debian
curl -L -o cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$(dpkg --print-architecture).deb
sudo dpkg -i cloudflared.deb
```

## Install as a Skill
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Ubuntu / Debian
curl -L -o cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-$(dpkg --print-architecture).deb
sudo dpkg -i cloudflared.deb
```

## Install as a Skill
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
- **Token storage**: Token refresh overwrites values in `.env` in plaintext. Never commit `.env` to version control.
- **Local file upload**: A temporary cloudflared tunnel exposes files during upload only. The tunnel shuts down immediately after. Only provide file paths you are comfortable briefly exposing.
- **Minimum permissions**: Create a dedicated Meta app and grant only: `instagram_business_basic`, `instagram_content_publish`, `instagram_manage_comments`, `pages_read_engagement`, `pages_show_list`.

## Requirements
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.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger text, 'Use when the user requests any Instagram-related task,' is broad enough to activate for vague or routine mentions of Instagram, increasing the chance the skill is invoked when not necessary. Over-broad triggering can cause unnecessary access to credentials, networked tools, or account-modifying actions in contexts where the user did not clearly intend that behavior.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The spec states that refreshed tokens are written back to the provided .env path, meaning credential files on disk are modified as a side effect of normal command execution. Without an explicit warning and safe-write guidance, this can surprise users, overwrite shared configuration, or leave sensitive tokens stored in insecure locations.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/_common.js:326

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/_common.js:51