Back to skill

Security audit

Vaultwarden Secrets

Security checks for vulnerabilities and agentic risk

Overview

This Vaultwarden helper is for real secret management, but it needs review because it can operate beyond the advertised collection scope and stores sensitive session or cached credential material on disk.

Install only if this agent is allowed to access and mutate every item available to the authenticated Bitwarden account, not just the openclaw collection. Prefer disabling the read cache with VW_CACHE_TTL=0, keep VW_SESSION_DIR private, manually verify the Bitwarden server configuration before unlocking, and avoid broad vault credentials until collection-membership checks are added.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/vw-update.sh:24
Finding

Collection Scope Is Not Enforced for Direct Item Operations

Content
View full analysis
/dev/null) if [ -z "$CURRENT" ]; then echo "error: item '$ITEM_ID' not found" >&2 _vw_log "update" "$ITEM_ID:$FIELD" "not-found" exit 1 fi ITEM_NAME=$(echo "$CURRENT" | jq -r '.name') echo "$UPDATED" | bw encode | bw edit item "$ITEM_ID" > /dev/null ``` `scripts/vw-delete.sh:18-32`: ```bash # Fetch and confirm name matches ACTUAL_NAME=$(bw get item "$ITEM_ID" 2>/dev/null | jq -r '.name') if [ -z "$ACTUAL_NAME" ] || [ "$ACTUAL_NAME" = "null" ]; then echo "error: item '$ITEM_ID' not found" >&2 _vw_log "delete" "$ITEM_ID" "not-found" exit 1 fi if [ "$ACTUAL_NAME" != "$EXPECTED_NAME" ]; then echo "error: name mismatch — expected '$EXPECTED_NAME', got '$ACTUAL_NAME'. Delete aborted." >&2 _vw_log "delete" "$ITEM_ID" "name-mismatch" exit 1 fi bw delete item "$ITEM_ID" ``` `scripts/vw-rotate-pass.sh:14-35`: ```bash # Resolve item ITEM=$(bw get item "$1" 2>/dev/null) if [ -z "$ITEM" ]; then echo "error: item '$1' not found" >&2 _vw_cache_clear _vw_log "rotate-pass" "$1" "not-found" exit 1 fi ITEM_ID=$(echo "$ITEM" | jq -r '.id') ITEM_NAME=$(echo "$ITEM" | jq -r '.name') # Generate new password NEW_PASS=$(bw generate --length "$LENGTH" --uppercase --lowercase --number --special) # Upda ...[truncated 2340 chars]
Remediation
View remediation
&2 exit 1 fi fi ``` 3. Apply the check consistently in `vw-get.sh`, `vw-get-field.sh`, `vw-get-totp.sh`, `vw-update.sh`, `vw-delete.sh`, and `vw-rotate-pass.sh`. 4. For TOTP operations, first retrieve and authorize the item, then request the TOTP using the validated item ID. 5. Make full-vault fallback an explicit configuration option rather than silently enabling it when collection lookup fails. 6. Distinguish a legitimate personal-vault result from transient lookup, authentication, parsing, or network failures. Fail closed when the reason for an empty collection ID is uncertain. 7. Add tests proving that out-of-collection item IDs are rejected for every operation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/_vw-session.sh:90
Finding

Collision-Prone Plaintext Secret Cache Can Return the Wrong Credential

Content
View full analysis
/dev/null || echo 0) )) [ "$age" -gt "${CACHE_TTL}" ] && { rm -f "$cache_file"; return 1; } cat "$cache_file" } _vw_cache_set() { [ "${CACHE_TTL}" -eq 0 ] && cat && return 0 local key="$1" mkdir -p "$CACHE_DIR" chmod 700 "$CACHE_DIR" local cache_file="$CACHE_DIR/$(echo "$key" | tr -cs '[:alnum:]' '_')" tee "$cache_file" chmod 600 "$cache_file" } ``` `scripts/vw-get-pass.sh:12-30`: ```bash CACHED=$(_vw_cache_get "pass_$1") && { echo "$CACHED"; _vw_log "get-password" "$1" "cache-hit"; exit 0; } COLLECTION_ID=$(_vw_collection_id) if [ -n "$COLLECTION_ID" ]; then RESULT=$(bw list items --collectionid "$COLLECTION_ID" --search "$1" 2>/dev/null | \ jq -r --arg name "$1" '.[] | select(.name==$name) | .login.password // empty' | head -1) else RESULT=$(bw list items --search "$1" 2>/dev/null | \ jq -r --arg name "$1" '.[] | select(.name==$name) | .login.password // empty' | head -1) fi if [ -z "$RESULT" ]; then echo "error: password for '$1' not found" >&2 _vw_log "get-password" "$1" "not-found" exit 1 fi echo "$RESULT" | _vw_cache_set "pass_$1" ``` ### Technical Analysis Cache filenames are generated by replacing every sequence of non-alphanumeric characters with an underscore. This transformation is not injective: different item names can produce the same filename. For ...[truncated 1919 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/vw-unlock.sh:40
Finding

Credential-Bearing Operations Trust an Unvalidated Configured Server

Content
View full analysis
/dev/null | jq -r '.serverUrl // empty') if [ -z "$SERVER_URL" ]; then echo "error: server not configured — run: bw config server https://vaultwarden.mbojer.dk" exit 1 fi # Login via API key if needed STATUS=$(bw status | jq -r '.status' 2>/dev/null || echo "unauthenticated") if [ "$STATUS" = "unauthenticated" ]; then if ! bw login --apikey --quiet 2>/dev/null; then echo "error: API key login failed — check BW_CLIENTID and BW_CLIENTSECRET" echo "error: if error contains 'User Decryption Options', downgrade CLI: npm install -g @bitwarden/cli@2023.10.0" exit 1 fi fi SESSION=$(bw unlock --passwordenv BW_PASSWORD --raw 2>/dev/null || true) ``` The documentation specifies the expected endpoint: ```bash bw config server https://vaultwarden.mbojer.dk ``` ### Technical Analysis The script only verifies that `serverUrl` is non-empty. It does not require HTTPS or compare the active Bitwarden CLI endpoint against the documented trusted Vaultwarden server. The Bitwarden CLI server configuration is mutable state external to this Skill. If that state is changed, `bw login --apikey` and later synchronization or item operations will target the substituted endpoint. The sensitive data transmission detected in `vw-update.sh` is functionally necessary for remote vault updates, but its destination inherits this unvalidated configuration. This is not evidence of hidden exfiltration by `vw-update.sh`: the value is sent through the expected `bw edit item` operation. The security issue is that the wrapper does not establish that `bw` is connected to the intended trusted host before initiating credential-bearing network operations. ### Attack Path 1. An attack ...[truncated 1342 chars]
Remediation
View remediation
&2 exit 1 fi ``` 5. Perform this validation before every credential-bearing operation, or centralize all scripts behind a session-loading function that validates the server. 6. Protect the Bitwarden CLI configuration directory with appropriate ownership and permissions. 7. Document any intentional endpoint migration procedure rather than silently trusting arbitrary preconfigured state. 8. Use certificate validation normally provided by HTTPS and consider additional endpoint controls, such as network allowlisting, for high-value deployments. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (19)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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).

Content

Scanner excerpt · scripts/_vw-session.sh (reported line 114)May include surrounding context.

sh
_vw_cache_clear() {
    local key="${1:-}"
    if [ -n "$key" ]; then
        rm -f "$CACHE_DIR/$(echo "$key" | tr -cs '[:alnum:]' '_')"
    else
        rm -f "$CACHE_DIR"/* 2>/dev/null || true
    fi

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% confidence
Finding

rm -f "$CACHE_DIR"/* deletes all entries under a path fully controlled by the CACHE_DIR variable, which can be overridden indirectly through SESSION_DIR. If an attacker can influence that environment variable when the script runs with higher privileges, they may trigger unintended bulk deletion in an arbitrary directory, causing destructive data loss.

Content

Scanner excerpt · scripts/_vw-session.sh (reported line 116)May include surrounding context.

sh
if [ -n "$key" ]; then
        rm -f "$CACHE_DIR/$(echo "$key" | tr -cs '[:alnum:]' '_')"
    else
        rm -f "$CACHE_DIR"/* 2>/dev/null || true
    fi
}

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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).

Content

Scanner excerpt · scripts/vw-lock.sh (reported line 16)May include surrounding context.

sh
fi

rm -f "$SESSION_DIR/.collection_id"
rm -rf "$SESSION_DIR/cache" 2>/dev/null || true
echo "ok: locked"

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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).

Content

Scanner excerpt · scripts/vw-lock.sh (reported line 15)May include surrounding context.

sh
bw sync --quiet

# Invalidate collection ID cache — may have changed
rm -f "$SESSION_DIR/.collection_id"

echo "ok: vault synced"
_vw_log "sync" "vault" "ok"

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
95% 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).

Content

Scanner excerpt · scripts/vw-sync.sh (reported line 13)May include surrounding context.

sh
bw sync --quiet

# Invalidate collection ID cache — may have changed
rm -f "$SESSION_DIR/.collection_id"

echo "ok: vault synced"
_vw_log "sync" "vault" "ok"

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

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

vw-sync.sh # sync local cache with server — run if vault modified externally

text

Session token stored in `$VW_SESSION_DIR/.bw_session` (chmod 600).
Collection ID cached in `$VW_SESSION_DIR/.collection_id` — invalidated on lock and sync.
Read cache stored in `$VW_SESSION_DIR/cache/` — TTL controlled by `VW_CACHE_TTL`.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation says reads are collection-scoped, but the skill also states that if no collection is found it silently falls back to unscoped full-vault queries. This can cause operators or downstream agents to assume least-privilege access while actually exposing secrets from the entire vault, increasing the chance of unintended secret disclosure.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The rules section states operations are scoped to the collection when available, but earlier text says missing collections trigger unscoped full-vault queries. That inconsistency creates a dangerous trust gap: users may perform secret operations believing they are constrained to a collection when the effective scope is the entire vault.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script claims to be scoped to a collection, but if no collection is configured it silently falls back to searching all items in the personal vault. That expands the trust boundary and can return secrets outside the expected scope, which is dangerous for a password-retrieval helper because callers may assume collection-only access when the script is actually broader.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script caches the retrieved password value directly, creating an additional plaintext copy of a secret outside the password manager. If the cache is readable by other users, persists longer than expected, or is logged/backed up, credentials can be exposed even after the original vault access ends.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The script retrieves a sensitive one-time password via bw get totp and then records the action with _vw_log, but it provides no visible disclosure, prompt, or warning to the user about handling sensitive authentication data. Because TOTP codes are highly sensitive and the script also logs the access event, this qualifies as a safety-relevant operation lacking user-facing warning in the file itself.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script is documented as collection-scoped, but when no collection ID is available it silently falls back to bw list items across the entire vault. That can expose secrets outside the intended openclaw boundary and violates least-privilege expectations, especially if downstream automation assumes results are safely scoped.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/_vw-session.sh (reported line 105)May include surrounding context.

sh
if [ ! -d "$SESSION_DIR" ]; then
    mkdir -p "$SESSION_DIR"
    chmod 700 "$SESSION_DIR"
fi

# Check existing session

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/vw-unlock.sh (reported line 26)May include surrounding context.

sh
if [ ! -d "$SESSION_DIR" ]; then
    mkdir -p "$SESSION_DIR"
    chmod 700 "$SESSION_DIR"
fi

# Check existing session

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script persists the Vaultwarden/Bitwarden session token to disk in a predictable location under /run/openclaw/vw/.bw_session. Although file permissions are tightened to 600 and the directory to 700, the token still becomes a reusable bearer secret at rest, which increases exposure through local compromise, backup/logging mistakes, container breakout, or unintended sharing between users/processes.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/_vw-session.sh (reported line 80)May include surrounding context.

sh
fi

echo "$SESSION" > "$SESSION_FILE"
chmod 600 "$SESSION_FILE"

export BW_SESSION="$SESSION"
echo "ok: unlocked (bw $VERSION)"

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/_vw-session.sh (reported line 86)May include surrounding context.

sh
fi

echo "$SESSION" > "$SESSION_FILE"
chmod 600 "$SESSION_FILE"

export BW_SESSION="$SESSION"
echo "ok: unlocked (bw $VERSION)"

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/_vw-session.sh (reported line 108)May include surrounding context.

sh
fi

echo "$SESSION" > "$SESSION_FILE"
chmod 600 "$SESSION_FILE"

export BW_SESSION="$SESSION"
echo "ok: unlocked (bw $VERSION)"

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · scripts/vw-unlock.sh (reported line 64)May include surrounding context.

sh
fi

echo "$SESSION" > "$SESSION_FILE"
chmod 600 "$SESSION_FILE"

export BW_SESSION="$SESSION"
echo "ok: unlocked (bw $VERSION)"

Static analysis

No suspicious patterns detected.