Back to skill

Security audit

Pr Ship

Security checks for vulnerabilities and agentic risk

Overview

The main PR-review skill is mostly read-only, but the package also includes under-documented maintenance tooling that can access OpenClaw cron configuration, contact GitHub, and optionally trigger a live cron job.

Install only if you want this OpenClaw-specific PR review workflow and are comfortable reviewing its reports for possible secrets from local diffs. Treat scripts/test-update-pipeline.sh and the provenance curl commands as maintainer/admin tooling, not routine review steps; avoid running the script, especially with --live, unless you have inspected your OpenClaw cron job and accept the side effects.

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

other

Warning
Location
scripts/test-update-pipeline.sh:195
Finding
Bundled Maintenance Script Exceeds the Advertised Read-Only Skill Scope<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test-update-pipeline.sh`, lines 6-7, 52-74, 144-181, 195-249, 336-350, and 381-390 **Vulnerability Type**: Excessive Scope and Environment Reconnaissance **Risk Level**: Medium ### Vulnerable Code ```bash SKILL_DIR="/home/dev/.openclaw/skills/pr-ship" OPENCLAW_DIR="/home/dev/openclaw" ``` ```bash cd "$SKILL_DIR" # Test .gitignore blocks .env files TRAP_FILE="$SKILL_DIR/.env.test-trap" echo "SECRET_KEY=do-not-commit" > "$TRAP_FILE" if git check-ignore -q "$TRAP_FILE" 2>/dev/null; then pass ".gitignore blocks .env files" else fail ".gitignore does not block .env files" fi rm -f "$TRAP_FILE" # Test .gitignore blocks swap files SWAP_FILE="$SKILL_DIR/SKILL.md.swp" touch "$SWAP_FILE" if git check-ignore -q "$SWAP_FILE" 2>/dev/null; then pass ".gitignore blocks .swp files" else warn ".gitignore does not block .swp files" fi rm -f "$SWAP_FILE" ``` ```bash cd "$OPENCLAW_DIR" git fetch upstream --quiet 2>/dev/null || true LOCAL_SHA=$(git rev-parse main:CHANGELOG.md 2>/dev/null || echo none) UPSTREAM_SHA=$(git rev-parse upstream/main:CHANGELOG.md 2>/dev/null || echo none) # ... cd "$SKILL_DIR" LOCAL_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "none") REMOTE_SHA=$(git ls-remote origin HEAD 2>/dev/null | cut -c1-7 || echo "none") ``` ```bash JOBS_FILE="$HOME/.openclaw/cron/jobs.json" if [ -f "$JOBS_FILE" ]; then JOB_MSG=$(python3 -c " import json jobs=json.load(open('$JOBS_FILE'))['jobs'] j=[x for x in jobs if x['id']=='492d067a-5cb1-47c5-92bc-fd8985c64a1f'] if j: print(j[0]['payload']['message']) else: print('NOT_FOUND') " 2>/dev/null || echo "PARSE_ERROR") if [ "$JOB_MSG" = "NOT_FOUND" ]; then fail "Cron job 492d067a not found in jobs.json" elif [ "$JOB_MSG" = "PARSE_ERROR" ]; then fail "Could not parse jobs.json" else pass "Cron job 492d067a found" if echo "$JOB_MSG" | grep -q "git push"; then fail "Cron still has git push — should be removed (manual sy ...[truncated 4510 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the maintenance script from the distributed Skill package unless it is required for end users. 2. Move the live cron trigger into a separately distributed administrative script with an explicit warning and confirmation prompt. 3. Split validation into distinct modes: - Offline, read-only validation by default. - Network validation only after explicit opt-in. - Live cron execution only through a separate command. 4. Replace hard-coded `/home/dev/...` paths with explicit command-line arguments or securely validated configuration. 5. Do not load the complete cron payload merely to validate job metadata. Parse only the minimum required fields and avoid retaining or printing sensitive message content. 6. Before executing a cron job, display its identifier and intended operation and require interactive confirmation. 7. Validate that the targeted job belongs to this Skill and restrict execution to an allowlisted, immutable operation. 8. Create temporary files with `mktemp` in a private temporary directory rather than writing trap files into the installed Skill directory. 9. Add cleanup traps so temporary files are removed on interruption or failure. 10. Document every filesystem, network, configuration-access, and live-execution side effect in README.md. 11. Avoid suppressing failures with `|| true` for network and live cron operations; return a clear error so users can determine whether an operation ran successfully. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill is presented as a read-only PR risk reporter, but the document also includes provenance/update verification workflows that use external network access and go beyond core diff analysis. That mismatch can cause operators or downstream agents to perform actions outside the expected trust boundary, increasing the chance of unintended data exposure or unsafe execution paths.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
| `console.log/warn/error` in runtime code | Diff adds `console.*` outside tests | Must use subsystem logger for runtime paths. |
| Try/catch on primary operation | Diff adds try/catch around core logic (not convenience wrapper) | Swallowing errors on critical paths hides bugs. |
| File locking removed | Diff removes `locked()`, `withLock`, lock wrappers | Race conditions in concurrent session/cron access. |
| `--force` in git operation | Diff or command uses `git push --force` | Data loss risk. Must use `--force-with-lease`. |
| New tool without policy entry | New tool file without matching policy configuration | Tool runs without access controls. |
| Protocol schema changed | Diff touches `gateway/protocol/schema/*.ts` | CLI/TUI client compatibility. Recommend running `pnpm protocol:gen:swift` + `pnpm protocol:check` before PR. |
Confidence
70% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
cd "$SKILL_DIR"

# Test .gitignore blocks .env files
TRAP_FILE="$SKILL_DIR/.env.test-trap"
echo "SECRET_KEY=do-not-commit" > "$TRAP_FILE"
if git check-ignore -q "$TRAP_FILE" 2>/dev/null; then
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
cd "$SKILL_DIR"

# Test .gitignore blocks .env files
TRAP_FILE="$SKILL_DIR/.env.test-trap"
echo "SECRET_KEY=do-not-commit" > "$TRAP_FILE"
if git check-ignore -q "$TRAP_FILE" 2>/dev/null; then
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
cd "$SKILL_DIR"

# Test .gitignore blocks .env files
TRAP_FILE="$SKILL_DIR/.env.test-trap"
echo "SECRET_KEY=do-not-commit" > "$TRAP_FILE"
if git check-ignore -q "$TRAP_FILE" 2>/dev/null; then
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
cd "$SKILL_DIR"

# Test .gitignore blocks .env files
TRAP_FILE="$SKILL_DIR/.env.test-trap"
echo "SECRET_KEY=do-not-commit" > "$TRAP_FILE"
if git check-ignore -q "$TRAP_FILE" 2>/dev/null; then
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
cd "$SKILL_DIR"

# Test .gitignore blocks .env files
TRAP_FILE="$SKILL_DIR/.env.test-trap"
echo "SECRET_KEY=do-not-commit" > "$TRAP_FILE"
if git check-ignore -q "$TRAP_FILE" 2>/dev/null; then
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
else
    fail ".gitignore missing archive exclusion"
  fi
  if grep -q ".env" .gitignore; then
    pass ".gitignore excludes .env files"
  else
    warn ".gitignore missing .env exclusion"
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The skill states that exploration commands are read-only and limited to grep/find/ls/git diff, but later provides curl-based examples that fetch remote content. This inconsistency weakens operator trust and can cause an automated agent to exceed its declared execution model, especially where network isolation or strict command allowlists are expected.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The provenance verification section instructs use of curl against GitHub APIs/raw content, which introduces outbound network access unrelated to generating a local PR risk report. In an agent setting, this can leak metadata about the local environment or repository usage and violates the principle of minimizing capabilities for a read-only local analysis skill.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Quick: compare file list + versions
diff <(clawhub list | grep pr-ship) <(curl -s https://api.github.com/repos/Glucksberg/pr-ship/contents/package.json | jq -r '.content' | base64 -d | jq -r .version)

# Full: diff your local install against GitHub
SKILL_DIR="$(find ~/.openclaw/skills -maxdepth 1 -name pr-ship -type d 2>/dev/null || echo skills/pr-ship)"
Confidence
93% confidence
Finding
The embedded curl command sends a request to api.github.com as part of a verification workflow. Even though the destination is legitimate, outbound requests from a skill can disclose installation details, timing, IP metadata, and encourage combining local state with remote lookups, which is risky for a tool advertised as local-only analysis.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill manifest describes this skill as producing a pre-ship risk report by dynamically exploring the codebase to assess module risk, blast radius, and version-specific gotchas. In contrast, package.json describes it as a "Pre-PR checklist and shipping workflow for OpenClaw," which suggests a checklist/workflow tool rather than a code-risk analysis skill. This is a semantic mismatch in the skill's claimed purpose and can mislead users about what the skill actually does.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
This markdown file contains natural-language policy that explicitly disallows 'Full-doc translation sets.' That is a language-policy constraint and it does not offer a language choice, opt-in path, or region-specific justification, so it matches the locale/language policy violation criteria.

Static analysis

No suspicious patterns detected.