Back to skill

Security audit

BrewPage Publish

Security checks for vulnerabilities and agentic risk

Overview

This publishing skill is coherent, but it needs review because it can upload arbitrary local content to a public service, stores owner tokens in a workspace file, and includes unsafe shell templates.

Install only if you are comfortable with an agent publishing selected content to brewpage.app. Use explicit slash-command invocations, review exactly which file or directory will be uploaded, avoid sensitive or source directories, prefer password protection when appropriate, and keep or move brewpage-history.md out of version control because it contains deletion tokens.

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

Error
Location
SKILL.md:135
Finding

Command Injection Through Unsafe Shell Template Substitution

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 135–138; related unsafe path substitutions at lines 194 and 223
Vulnerability Type: Shell command injection
Risk Level: High

Vulnerable Code

bash
CONTENT=$(cat <<'BREWPAGE_EOF'
{content}
BREWPAGE_EOF
)

Related path substitutions:

bash
(cd "{directory_path}" && zip -r "$TMPZIP" . -x '.git/*' '*/.git/*' '.env' '.env.*' '*/.env' '*/.env.*' 'node_modules/*' '*/node_modules/*' '.DS_Store' '*/.DS_Store' 'Thumbs.db' '.idea/*' '*/.idea/*' '.vscode/*' '*/.vscode/*' '.cache/*' '*/.cache/*' '*.map' '*.log')
bash
-F "archive=@{zip_file_path}")

Technical Analysis

The Skill instructs the Agent to construct executable shell source by directly replacing placeholders with user-controlled content or filesystem paths. Shell quoting does not make textual source-code substitution safe.

For text publication, an attacker-controlled input containing a line exactly equal to BREWPAGE_EOF can terminate the heredoc before its intended endpoint. Subsequent input is then parsed as shell syntax rather than assigned to CONTENT.

Directory and ZIP paths are likewise inserted inside double-quoted shell text. A path containing a double quote followed by shell metacharacters can escape the intended argument and introduce additional commands. These risks arise before jq or curl safely processes the values.

Attack Path

  1. An attacker supplies content to be published that includes the fixed heredoc terminator on its own line.
  2. The attacker places a shell command after that terminator.
  3. The Agent replaces {content} in the documented Bash block with the supplied text.
  4. The shell closes the heredoc at the attacker-provided terminator.
  5. The following text is interpreted and executed as shell commands under the Agent process's account.

An analogous path exists for a malicious directory or ZIP filename:

  1. The attacker creates or references a path containing qu ...[truncated 803 chars]
Remediation
View remediation

Remediation Suggestions

  • Do not interpolate user-controlled values into generated shell source.
  • Implement publishing in a fixed script that receives content and paths through positional parameters, standard input, or environment variables.
  • For text content, write the content to a securely created temporary file or pass it through standard input, then invoke jq without constructing a dynamic heredoc.
  • Pass paths as already-separated argument values, for example through script.sh -- "$path", rather than inserting them into a command string.
  • Canonicalize and validate paths before use, while still relying on argument separation rather than validation alone.
  • Avoid eval, dynamically generated shell fragments, and fixed heredoc delimiters around untrusted data.
  • Add regression tests using content containing BREWPAGE_EOF, command substitutions, quotes, semicolons, newlines, and filenames containing shell metacharacters.
  • If temporary files are used, create them with mktemp, restrictive permissions, and cleanup traps.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:111
Finding

Deletion-Capable Owner Tokens Stored in an Unprotected Plaintext Workspace File

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 111–127; token writes at lines 149–151, 172–174, 208–210, and 234–236
Vulnerability Type: Insecure storage of sensitive credentials
Risk Level: Medium

Vulnerable Code

bash
HISTORY_FILE="./brewpage-history.md"
if [ ! -f "$HISTORY_FILE" ]; then
  cat > "$HISTORY_FILE" <<'HEADER'
# brewpage.app — Published Pages

> PRIVATE FILE — keep this out of version control and never share it.
> Owner tokens allow delete (no in-place PUT for sites; html/json/kv support PUT).
> Delete html/json/kv: `curl -s -X DELETE "https://brewpage.app/api/{ns}/{id}" -H "X-Owner-Token: TOKEN"`
> Delete site:         `curl -s -X DELETE "https://brewpage.app/api/sites/{ns}/{id}" -H "X-Owner-Token: TOKEN"`

| Date | URL | Owner Token | TTL | Type |
|------|-----|-------------|-----|------|
HEADER
fi

The response token is then appended directly to that file:

bash
URL=$(echo "$RESPONSE" | jq -r '.link // empty')
TOKEN=$(echo "$RESPONSE" | jq -r '.ownerToken // empty')

if [ -n "$URL" ]; then
  [ -n "$TOKEN" ] && echo "| $(date '+%Y-%m-%d %H:%M') | [$URL]($URL) | \`$TOKEN\` | {ttl}d | html |" >> "$HISTORY_FILE"
  echo "OK $URL"
else
  echo "FAILED: $RESPONSE"
fi

Equivalent plaintext token writes are prescribed for file, directory-site, and ZIP-site publication.

Technical Analysis

The owner token is a bearer credential that authorizes deletion and, for some resource types, modification of published content. The Skill saves this credential in a predictable Markdown file inside the current workspace without setting restrictive permissions.

The implementation relies on the process umask and does not:

  • create the file with mode 0600;
  • verify file ownership;
  • reject symbolic links;
  • use atomic secure file creation;
  • place credentials outside the project tree; or
  • automatically exclude the file from version control.

The warning that the file is private does not ...[truncated 1847 chars]

Remediation
View remediation

Remediation Suggestions

  • Store owner tokens in an operating-system credential manager or dedicated secrets store rather than Markdown.
  • If file storage is unavoidable, use a private state directory outside the project tree with directory mode 0700 and file mode 0600.
  • Create the credential file atomically with restrictive permissions, such as by setting umask 077 before creation.
  • Check the destination with lstat and reject symbolic links and unexpected file types.
  • Verify that the file is owned by the current user before reading or appending to it.
  • Separate non-sensitive publication history from token storage so URLs can be tracked without exposing credentials.
  • Add brewpage-history.md to .gitignore automatically when legacy workspace storage is used, while noting that .gitignore is only an additional safeguard.
  • Provide a migration mechanism that moves existing plaintext tokens into secure storage and securely removes the old history file.
  • Minimize token retention and offer an explicit option not to persist owner tokens.
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

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

md
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
TMPZIP=$(mktemp /tmp/brewpage-site-XXXXXX.zip)
# Exclude VCS, secrets, deps, editor + OS junk and sourcemaps — publish only built static assets.
(cd "{directory_path}" && zip -r "$TMPZIP" . -x '.git/*' '*/.git/*' '.env' '.env.*' '*/.env' '*/.env.*' 'node_modules/*' '*/node_modules/*' '.DS_Store' '*/.DS_Store' 'Thumbs.db' '.idea/*' '*/.idea/*' '.vscode/*' '*/.vscode/*' '.cache/*' '*/.cache/*' '*.map' '*.log')
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/sites?ns={ns}&ttl={days}&entry={entry}" \
  -H "User-Agent: OpenClaw/1.0" \
  "${PASS_H[@]}" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The top-level description emphasizes convenience and 'instant public URL' but does not prominently warn that uploaded files, text, or sites become publicly accessible on the internet. For a hosting/publishing skill, insufficient disclosure materially increases the risk that users expose sensitive documents, source artifacts, or personal data by mistake.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.md (reported line 30)May include surrounding context.

OpenClaw discovers skills from two locations. Drop the brewpage-publish/ folder (containing SKILL.md) into either:

text
<workspace>/skills/brewpage-publish/SKILL.md     # project-local skill
~/.openclaw/skills/brewpage-publish/SKILL.md      # user-global skill

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

Scanner excerpt · README.md (reported line 31)May include surrounding context.

OpenClaw discovers skills from two locations. Drop the brewpage-publish/ folder (containing SKILL.md) into either:

text
<workspace>/skills/brewpage-publish/SKILL.md     # project-local skill
~/.openclaw/skills/brewpage-publish/SKILL.md      # user-global skill

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The natural-language invocation examples are broad enough that an agent may trigger this publishing skill from ordinary user requests like 'share this file' or 'upload report.md' without the user fully appreciating that the destination is a public hosting service. In the context of a skill that publishes arbitrary content to a public URL, unintended invocation can directly lead to accidental data exposure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger list is very broad and includes generic phrases like 'share link', 'share a file', 'upload site', and 'publish'. That increases the chance of unintended invocation, causing users to trigger public upload behavior when they may have only meant local sharing or a different action.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill markets 'free instant hosting' and 'share a file' but does not place a prominent upfront warning that the action uploads content to a third-party public service and may expose it publicly. In this context, the omission is dangerous because the skill handles arbitrary files and directories, so users may disclose sensitive data without fully informed consent.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill explicitly instructs the agent to persist owner tokens in a local markdown history file. Even if not printed to chat, these tokens are credential-like secrets that authorize deletion and possibly management actions, so storing them in a workspace file increases the attack surface through accidental commit, later exfiltration by other tools, or disclosure to the user without clear consent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
98% confidence
Finding

This skill transmits user-provided content to an external domain via curl, creating a real data exfiltration path by design. External transmission is not inherently malicious here, but it is security-relevant because the skill supports arbitrary text, files, and sites, making accidental publication of confidential material a substantial risk.

Content

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

md
PAYLOAD=$(jq -n --arg c "$CONTENT" '{content: $c}')
PASS_H=()
[ -n "$PASSWORD" ] && PASS_H=(-H "X-Password: $PASSWORD")
RESPONSE=$(curl -s -X POST "https://brewpage.app/api/html?ns={ns}&ttl={days}&format=markdown" \
  -H "Content-Type: application/json" \
  "${PASS_H[@]}" \
  -d "$PAYLOAD")

Static analysis

No suspicious patterns detected.