Back to skill

Security audit

Social Media Autopilot

Security checks for vulnerabilities and agentic risk

Overview

This social-media publishing skill is purpose-aligned, but its scripts have serious input-handling and publication-integrity flaws that could execute unintended local code or falsely mark failed posts as published.

Review this skill before installing, especially if it will publish to real brand or personal accounts. Do not run it on untrusted post text or untrusted post IDs, avoid enabling auto_approve, and treat OAuth tokens as sensitive credentials. The scripts should be fixed to safely encode JSON, validate post IDs and paths, preserve drafts on publication failure, and verify platform API responses before being used for production posting.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/draft-post.sh:69
Finding

Arbitrary Python Code Execution Through Draft Post Text

Content
View full analysis
"$DRAFTS_DIR/$POST_ID.json" << EOF { "id": "$POST_ID", "platforms": $PLATFORMS_JSON, "text": $(python3 -c "import json; print(json.dumps('''$TEXT'''))"), "media": $MEDIA_JSON, "scheduled_at": $SCHEDULE_JSON, "status": "draft", "created_at": "$NOW", "approved": false, "tags": $TAGS_JSON, "thread": $THREAD } EOF ``` ### Technical Analysis The value supplied through `--text` is interpolated directly into Python source code inside a triple-quoted string. Shell quoting does not make the resulting Python program safe. An attacker who can influence the post text can insert a triple-quote terminator and additional Python statements. When `draft-post.sh` invokes `python3 -c`, the injected statements execute with the same operating-system privileges and environment as the user running the skill. This is not limited to corrupting the generated JSON. Injected Python can invoke system commands, read or modify files accessible to the current user, inspect environment variables containing social-media tokens, or establish additional network connections. ### Attack Path 1. An attacker supplies or causes the agent to use malicious content as the `--text` value. 2. The content closes the `'''...'''` Python string used by the script. 3. The content appends valid Python statements and comments out the remaining generated source. 4. `python3 -c` parses the attacker-controlled statements as executable code. 5. The payload runs with the privileges and environment of the skill process. A conceptual payload has the following structure: ```text '''); ATTACKER_CONTROLLED_PYTHON; # ``` ### Impact Assessment Successful exploitation provides arbitrary local code execution as the account running the skill. The attacker could: - Read or alter files available to that acc ...[truncated 414 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/approve-post.sh:5
Finding

Unvalidated Post IDs Permit Path Traversal and Unsafe Python Source Construction

Content
View full analysis
"; exit 1; } DRAFT_FILE="$BASE_DIR/drafts/$POST_ID.json" [ ! -f "$DRAFT_FILE" ] && { echo "❌ Draft not found: $POST_ID"; exit 1; } python3 -c " import json with open('$DRAFT_FILE') as f: post = json.load(f) post['approved'] = True post['approved_at'] = '$(date -u +%Y-%m-%dT%H:%M:%SZ)' with open('$DRAFT_FILE', 'w') as f: json.dump(post, f, indent=2) print(f'✅ Post {post[\"id\"]} approved') print(f' Platforms: {\", \".join(post[\"platforms\"])}') print(f' Text: {post[\"text\"][:100]}...' if len(post['text']) > 100 else f' Text: {post[\"text\"]}') " "$DRAFT_FILE" ``` The same unsafe pattern is also present in: ```bash # scripts/publish-post.sh DRAFT_FILE="$DRAFTS_DIR/$POST_ID.json" [ ! -f "$DRAFT_FILE" ] && { echo "❌ Draft not found: $POST_ID"; exit 1; } APPROVED=$(python3 -c "import json; d=json.load(open('$DRAFT_FILE')); print(d.get('approved', False))") PLATFORMS=$(python3 -c "import json; d=json.load(open('$DRAFT_FILE')); print(' '.join(d['platforms']))") TEXT=$(python3 -c "import json; d=json.load(open('$DRAFT_FILE')); print(d['text'])") ``` ```bash # scripts/analytics.sh POST_ID="$PARAM" PUBLISHED_FILE="$PUBLISHED_DIR/$POST_ID.json" if [ -f "$PUBLISHED_FILE" ]; then echo "📊 Post Analytics: $POST_ID" python3 -c " import json with open('$PUBLISHED_FILE') as f: post = json.load(f) ``` ### Technical Analysis The scripts treat a user-supplied post ID as a filename component without validating that it is a UUID or even a simple basename. Values containing `../` can escape the intended `drafts` or `published` directory. The scripts append `.json`, but this does not prevent traversal ...[truncated 1916 chars]
Remediation
View remediation
&2 exit 1 fi ``` Apply this validation consistently in `approve-post.sh`, `publish-post.sh`, and `analytics.sh`. Canonicalize the resulting path and verify that it remains under the expected directory before reading, writing, or deleting it. Do not rely only on string prefixes without canonicalization. Pass paths to Python as arguments rather than embedding them into source: ```bash python3 - "$DRAFT_FILE" <<'PY' import json import sys draft_file = sys.argv[1] with open(draft_file, encoding="utf-8") as handle: post = json.load(handle) PY ``` Before deletion, repeat the containment check and ensure that the target is a regular file rather than a symbolic link. Consider opening files with protections against symlink traversal where the runtime environment supports them. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/publish-post.sh:43
Finding

LinkedIn API Request Body Injection Through Unescaped Post Text

Content
View full analysis
&1) || true echo " LinkedIn response: $RESPONSE" else echo " ⚠️ LINKEDIN_ACCESS_TOKEN not set." fi ``` ### Technical Analysis `TEXT` is inserted directly into a JSON document without JSON escaping. Post text containing double quotes, backslashes, control characters, or crafted JSON fragments can terminate the intended string or make the document invalid. Approval does not neutralize the content. Once the draft is approved, `publish-post.sh` retrieves the stored text and places it into the API request exactly as shown. This flaw primarily enables manipulation or corruption of the JSON sent to LinkedIn. It is not shell command injection because the variable is expanded inside a quoted shell argument, but it can alter the structure and semantics of the remote API request. ### Attack Path 1. An attacker controls or influences draft text containing JSON metacharacters. 2. A user approves the draft. 3. `publish-post.sh` reads the malicious text into `TEXT`. 4. The script concatenates the text into a manually constructed JSON body. 5. The resulting request is malformed or contains attacker-influenced JSON structure. 6. LinkedIn rejects the request or processes un ...[truncated 564 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
references/instagram-setup.md:18
Finding

OAuth Credentials Exposed Through URLs and Command-Line Arguments in Setup Guidance

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/publish-post.sh:30
Finding

Failed Publications Are Falsely Archived as Successful and Source Drafts Are Deleted

Content
View full analysis
/dev/null; then RESPONSE=$(xurl post tweets -d "{\"text\": $(python3 -c "import json; print(json.dumps('$TEXT'))")}" 2>&1) || true echo " X response: $RESPONSE" else echo " ⚠️ xurl not found. Set up X API credentials or install xurl skill." fi ;; linkedin) if [ -n "${LINKEDIN_ACCESS_TOKEN:-}" ]; then RESPONSE=$(curl -s -X POST "https://api.linkedin.com/v2/ugcPosts" \ -H "Authorization: Bearer $LINKEDIN_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"author\": \"urn:li:person:${LINKEDIN_PERSON_ID:-me}\", \"lifecycleState\": \"PUBLISHED\", \"specificContent\": { \"com.linkedin.ugc.ShareContent\": { \"shareCommentary\": {\"text\": \"$TEXT\"}, \"shareMediaCategory\": \"NONE\" } }, \"visibility\": {\"com.linkedin.ugc.MemberNetworkVisibility\": \"PUBLIC\"} }" 2>&1) || true echo " LinkedIn response: $RESPONSE" else echo " ⚠️ LINKEDIN_ACCESS_TOKEN not set." fi ;; instagram) if [ -n "${INSTAGRAM_ACCESS_TOKEN:-}" ] && [ -n "${INSTAGRAM_BUSINESS_ID:-}" ]; then echo " ⚠️ Instagram requires media. Use the media workflow." else echo " ⚠️ Instagram credentials not set." fi ;; *) echo " ⚠️ Unknown platform: $platform" ;; esac done # Move to published python3 -c " import json with open('$DRAFT_FILE') as f: post = json.load(f) post['status'] ...[truncated 2134 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/instagram-setup.md (reported line 13)May include surrounding context.

  • Note your Client ID and Client Secret from the Auth tab
  • Add redirect URL: http://localhost:3000/callback (or your domain)

3. Generate Access Token

Request authorization:

text
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT&scope=openid%20profile%20w_member_social

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/instagram-setup.md (reported line 17)May include surrounding context.

  • Note your Client ID and Client Secret from the Auth tab
  • Add redirect URL: http://localhost:3000/callback (or your domain)

3. Generate Access Token

Request authorization:

text
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT&scope=openid%20profile%20w_member_social

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/linkedin-setup.md (reported line 13)May include surrounding context.

  • Note your Client ID and Client Secret from the Auth tab
  • Add redirect URL: http://localhost:3000/callback (or your domain)

3. Generate Access Token

Request authorization:

text
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT&scope=openid%20profile%20w_member_social

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill instructs the agent to run shell scripts and read/write workspace files, but the manifest does not declare any explicit tool scope or permission boundaries. This increases the chance the skill will be granted broader-than-necessary file and shell access, making accidental misuse or prompt-injection-driven abuse more likely.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

The auto_approve: true exception weakens the human-approval control for publishing and enables autonomous posting to external accounts. In a social-media publishing skill, this is risky because a prompt-injected, mistaken, or maliciously crafted draft could be published directly to public brand channels without a final user check.

Content

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

md
3. Only approved posts get published
4. Use `scripts/approve-post.sh <post-id>` or `scripts/reject-post.sh <post-id> --reason "..."`

**Never auto-publish without explicit approval** unless the user has configured `auto_approve: true` in `social-media/config.json`.

### 3. Publishing

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This markdown file instructs the user to generate, exchange, and store long-lived Instagram access tokens, including passing tokens in curl command lines and exporting them as environment variables. The document does not warn that these values are sensitive credentials that may be exposed via shell history, process lists, logs, or shared terminals.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/instagram-setup.md (reported line 21)May include surrounding context.

  • Exchange for a long-lived token (60 days):
bash
curl "https://graph.facebook.com/v18.0/oauth/access_token?grant_type=fb_exchange_token&client_id=APP_ID&client_secret=APP_SECRET&fb_exchange_token=SHORT_LIVED_TOKEN"

3. Get Instagram Business Account ID

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/linkedin-setup.md (reported line 21)May include surrounding context.

Exchange code for token:

bash
curl -X POST https://www.linkedin.com/oauth/v2/accessToken \
  -d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=YOUR_REDIRECT&client_id=YOUR_CLIENT_ID&client_secret=YOUR_SECRET"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This shell script creates a new JSON draft file and, when scheduling is used, also updates a calendar JSON file. Although it prints success messages after the fact, there is no prior warning, confirmation, or explanatory comment/docstring disclosing that the skill will write persistent data under the user's social-media workspace.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · scripts/init-workspace.sh (reported line 21)May include surrounding context.

sh
if [ ! -f "$BASE_DIR/config.json" ]; then
  cat > "$BASE_DIR/config.json" << 'EOF'
{
  "auto_approve": false,
  "default_platforms": ["x"],
  "timezone": "America/Los_Angeles",
  "posting_hours": {"start": 9, "end": 18},

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish-post.sh (reported line 44)May include surrounding context.

sh
;;
    linkedin)
      if [ -n "${LINKEDIN_ACCESS_TOKEN:-}" ]; then
        RESPONSE=$(curl -s -X POST "https://api.linkedin.com/v2/ugcPosts" \
          -H "Authorization: Bearer $LINKEDIN_ACCESS_TOKEN" \
          -H "Content-Type: application/json" \
          -d "{

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/publish-post.sh (reported line 44)May include surrounding context.

sh
;;
    linkedin)
      if [ -n "${LINKEDIN_ACCESS_TOKEN:-}" ]; then
        RESPONSE=$(curl -s -X POST "https://api.linkedin.com/v2/ugcPosts" \
          -H "Authorization: Bearer $LINKEDIN_ACCESS_TOKEN" \
          -H "Content-Type: application/json" \
          -d "{

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The --report path creates and writes to a file in the analytics directory, which is a state-changing filesystem operation. While the script prints the saved path after writing, there is no prior user-facing warning or confirmation that running this mode will create or overwrite a local report file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The default configuration sets "timezone": "America/Los_Angeles", which imposes a specific locale choice in a user-facing config template. The file does not indicate that this is optional at creation time or explain why this region-specific default is required, so it may violate language/locale policy expectations.

Content

No source excerpt is available for this finding.