Back to skill

Security audit

Arc Shield

Security checks for vulnerabilities and agentic risk

Overview

Arc-shield is a coherent local secret-scanning skill, but its advertised safety controls are unreliable and can expose the very secrets users expect it to block.

Install only if you treat this as an experimental scanner, not a dependable leak-prevention boundary. Do not route real secrets through its report mode, and do not rely on --strict or the Python entropy controls to guarantee blocking until the implementation is fixed and tested.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/arc-shield.sh:251
Finding
Strict mode emits sensitive input before blocking it<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arc-shield.sh:251-256`; `scripts/output-guard.py:267-273` **Vulnerability Type**: Fail-open sensitive-data disclosure **Risk Level**: High ### Vulnerable Code ```bash elif [[ "$MODE" == "strict" ]]; then echo "$INPUT" if [[ $FOUND_CRITICAL -gt 0 ]]; then echo -e "\n${RED}[BLOCKED]${NC} Critical secrets detected. Message blocked." >&2 exit 1 fi ``` ```python elif args.strict: print(text) critical_count = len([f for f in guard.findings if f.severity == CRITICAL]) if critical_count > 0: guard.print_report() print("\n[BLOCKED] Critical secrets detected. Message blocked.", file=sys.stderr) sys.exit(1) ``` ### Technical Analysis Both implementations write the complete, unredacted input to standard output before checking whether critical findings require the message to be blocked. Process exit status does not retract data already written to stdout. This violates the expected behavior of a strict output security boundary. Any caller that captures, logs, pipes, or forwards scanner output can receive the secret even though the scanner subsequently returns a failure status. ### Attack Path 1. An outbound message contains a recognized credential, such as a GitHub personal access token. 2. The message is passed to either scanner with `--strict`. 3. The scanner prints the original message, including the credential, to stdout. 4. A parent process, pipeline, hook, command substitution, or logging system captures that output. 5. The scanner exits with status 1 only after the sensitive content has already been disclosed. ### Impact Assessment No additional operating-system privileges are obtained. The affected scope is the full input message, including any credentials, private keys, PII, or tokens it contains. Disclosure can occur to downstream processes, terminal capture, CI output, hook consumers, or external messaging integrations. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Complete detection before writing any message content. - In strict mode, emit no stdout when a blocking finding exists. - Print the original input only after confirming that no finding meets the blocking threshold. - Ensure diagnostics contain categories and counts only, not matched values. - Add tests asserting that stdout is empty when strict mode returns a blocking status. - Apply the same fail-closed behavior to both Bash and Python implementations. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/arc-shield.sh:158
Finding
Bash scanner terminates on the first finding because of arithmetic exit status<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arc-shield.sh:5`, `scripts/arc-shield.sh:158-160` **Vulnerability Type**: Premature termination and unreliable security enforcement **Risk Level**: High ### Vulnerable Code ```bash set -euo pipefail ``` ```bash case $severity in CRITICAL) ((FOUND_CRITICAL++)) ;; HIGH) ((FOUND_HIGH++)) ;; WARN) ((FOUND_WARN++)) ;; esac ``` ### Technical Analysis In Bash, the exit status of an arithmetic expression is 1 when its resulting expression value is zero. For a post-increment such as `((FOUND_CRITICAL++))`, the expression evaluates to the previous value. On the first finding, that previous value is zero, so the command returns status 1. Because the script enables `set -e`, this status can terminate the script immediately. Execution may stop before reporting, redaction, strict-mode handling, or intended output behavior is completed. The resulting failure is incidental rather than an intentional policy decision, making scanner behavior dependent on shell error semantics. ### Attack Path 1. An input contains any pattern recognized as CRITICAL, HIGH, or WARN. 2. `report_finding` executes the corresponding post-increment while the counter is zero. 3. The arithmetic command returns status 1. 4. `set -e` terminates the script. 5. The selected report, redaction, or strict-mode logic does not complete, and the caller receives incomplete or unexpected output. ### Impact Assessment No elevated system privilege is obtained. The affected scope includes all Bash scanner modes. Redaction can return empty or incomplete output, reports can terminate before showing results, and integrations can make unsafe decisions based on unexpected process behavior. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Replace post-increments with expressions that return success after incrementing: ```bash ((++FOUND_CRITICAL)) ((++FOUND_HIGH)) ((++FOUND_WARN)) ``` - Alternatively, use assignments that are not interpreted as failing arithmetic commands: ```bash FOUND_CRITICAL=$((FOUND_CRITICAL + 1)) ``` - Add tests for the first CRITICAL, HIGH, and WARN finding in every supported mode. - Assert expected stdout, stderr, and exit status separately. - Avoid relying on `set -e` for security policy enforcement; return explicit statuses from mode-handling logic. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/output-guard.py:267
Finding
Python strict mode does not block high-entropy secrets<![CDATA[ ## Vulnerability Details **File Location**: `scripts/output-guard.py:153-156`, `scripts/output-guard.py:267-273`; `examples/send-safe-message.sh:29-34` **Vulnerability Type**: Security-control bypass caused by inconsistent severity handling **Risk Level**: High ### Vulnerable Code ```python high_entropy = self.detect_high_entropy_strings(text) for value, position in high_entropy: finding = Finding(HIGH, "High Entropy String", value, position) self.findings.append(finding) ``` ```python elif args.strict: print(text) critical_count = len([f for f in guard.findings if f.severity == CRITICAL]) if critical_count > 0: guard.print_report() print("\n[BLOCKED] Critical secrets detected. Message blocked.", file=sys.stderr) sys.exit(1) ``` The integration expects strict mode to block entropy findings: ```bash if ! echo "$MESSAGE" | python3 "$OUTPUT_GUARD" --strict > /dev/null 2>&1; then echo "❌ BLOCKED: High-entropy secret detected" >&2 echo "$MESSAGE" | python3 "$OUTPUT_GUARD" --report >&2 exit 1 fi ``` ### Technical Analysis Entropy-based detections are assigned `HIGH` severity, while strict mode only counts `CRITICAL` findings when deciding whether to return failure. Consequently, a novel credential detected solely through entropy analysis is recorded but does not cause strict mode to block the message. The example wrapper incorrectly assumes that a failed Python strict check indicates a high-entropy finding. Under the current implementation, entropy-only findings produce a successful exit status. ### Attack Path 1. An attacker or accidental output supplies a high-entropy credential that does not match any fixed CRITICAL regex. 2. Entropy analysis records it as `HIGH`. 3. Strict mode counts only `CRITICAL` findings. 4. The scanner exits successfully. 5. The safe-message wrapper treats the message as approved and permits it to proceed to the sending stage. ### Impact Assessment No additional lo ...[truncated 245 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Define a clear blocking threshold and enforce it consistently. - If strict mode is intended to block HIGH and CRITICAL findings, use logic such as: ```python blocking = [ finding for finding in guard.findings if finding.severity in {CRITICAL, HIGH} ] if blocking: sys.exit(1) ``` - Alternatively, classify entropy findings that satisfy the secret heuristics as CRITICAL. - Do not print the original input when any blocking finding exists. - Add an integration test containing only an entropy-detected value and assert a nonzero exit status. - Document whether WARN, HIGH, and CRITICAL findings are blocked in each mode. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
examples/send-safe-message.sh:22
Finding
Diagnostic reports disclose the detected secret values<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arc-shield.sh:178-184`; `scripts/output-guard.py:26-30`, `scripts/output-guard.py:216-224`; `examples/send-safe-message.sh:22-32` **Vulnerability Type**: Plaintext sensitive-data exposure through logs and diagnostics **Risk Level**: Medium ### Vulnerable Code ```bash echo -e "${color}[${severity}]${NC} ${category}" >&2 echo "$matches" | while IFS= read -r match; do # Truncate long matches for display if [[ ${#match} -gt 60 ]]; then match="${match:0:57}..." fi echo " → ${match}" >&2 done ``` ```python def __repr__(self): truncated = self.value[:60] + "..." if len(self.value) > 60 else self.value return f"[{self.severity}] {self.category}: {truncated}" ``` ```python if report['summary']['critical'] > 0: print("\nCRITICAL FINDINGS:", file=sys.stderr) for finding in report['findings']['critical']: print(f" {finding}", file=sys.stderr) if report['summary']['high'] > 0: print("\nHIGH FINDINGS:", file=sys.stderr) for finding in report['findings']['high']: print(f" {finding}", file=sys.stderr) ``` The safe-send example invokes these reports after detecting a secret: ```bash if ! echo "$MESSAGE" | "$ARC_SHIELD" --strict > /dev/null 2>&1; then echo "❌ BLOCKED: Message contains critical secrets (regex detection)" >&2 echo "$MESSAGE" | "$ARC_SHIELD" --report >&2 exit 1 fi if ! echo "$MESSAGE" | python3 "$OUTPUT_GUARD" --strict > /dev/null 2>&1; then echo "❌ BLOCKED: High-entropy secret detected" >&2 echo "$MESSAGE" | python3 "$OUTPUT_GUARD" --report >&2 exit 1 fi ``` ### Technical Analysis Both reporting implementations include plaintext portions of matched values. Truncation is not adequate secret protection because many credentials remain usable or identifiable within the first 60 characters. The integration example automatically runs report mode on messages already known to contain sensitive data. Since repor ...[truncated 808 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Redact finding values in reports by default. - Report only severity, category, count, and safe source position. - If correlation is required, display a keyed or one-way fingerprint rather than the credential itself. - Require an explicit, prominently unsafe diagnostic option before displaying matched content. - Remove automatic report invocation from blocked-message integrations. - Ensure CI and production logs never receive original message text. - Add tests that verify known secrets do not appear in stdout or stderr. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/arc-shield.sh:58
Finding
Combining strict and redact options silently disables strict blocking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arc-shield.sh:58-61`; documented unsafe use at `SKILL.md:92-96` **Vulnerability Type**: Argument-parsing logic flaw causing policy bypass **Risk Level**: High ### Vulnerable Code ```bash while [[ $# -gt 0 ]]; do case $1 in --strict) MODE="strict"; shift ;; --redact) MODE="redact"; shift ;; --report) MODE="report"; shift ;; --quiet) SEVERITY_THRESHOLD="CRITICAL"; shift ;; ``` The documented integration combines the options: ```bash SANITIZED=$(echo "$MESSAGE" | arc-shield.sh --strict --redact) EXIT_CODE=$? if [[ $EXIT_CODE -eq 1 ]]; then echo "ERROR: Message contains critical secrets and was blocked." >&2 exit 1 fi ``` ### Technical Analysis Strictness and output behavior are represented by one `MODE` variable. Each option overwrites the prior value. With the documented argument order, `--redact` replaces `--strict`, so the script runs only in redaction mode. Redaction mode returns success rather than enforcing strict blocking. The documented caller therefore cannot rely on `EXIT_CODE` to determine whether a critical secret was detected. This is especially dangerous where redaction coverage is incomplete, because an unredacted secret can remain in output while the process still reports success. ### Attack Path 1. An integrator follows the documented `--strict --redact` example. 2. Argument parsing first sets `MODE` to `strict`. 3. Parsing `--redact` overwrites `MODE` with `redact`. 4. A message contains a critical secret, including one not handled by the redaction substitutions. 5. The script executes redaction mode and returns success. 6. The wrapper skips its blocking branch and sends or stores the resulting message. ### Impact Assessment No local privilege escalation occurs. The security boundary for outbound messages is bypassed. The affected scope includes every integration that combines these options and assumes it receives both redaction ...[truncated 29 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Represent strictness and output behavior with independent flags: ```bash STRICT=0 OUTPUT_MODE="scan" --strict) STRICT=1 ;; --redact) OUTPUT_MODE="redact" ;; --report) OUTPUT_MODE="report" ;; ``` - Perform detection once, apply the selected output transformation, and independently return a blocking status when strict policy is violated. - If combinations are unsupported, reject them explicitly rather than silently applying the last option. - Ensure redaction covers every finding type before treating transformed output as safe. - Add tests for both `--strict --redact` and `--redact --strict`, verifying identical documented behavior. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/output-guard.py:248
Finding
Documented custom configuration and entropy controls are ignored<![CDATA[ ## Vulnerability Details **File Location**: `scripts/arc-shield.sh:9`; `scripts/output-guard.py:248-259` **Vulnerability Type**: Ineffective security configuration and false assurance **Risk Level**: Medium ### Vulnerable Code The Bash scanner defines a configuration path: ```bash CONFIG_FILE="${ARC_SHIELD_CONFIG:-${SCRIPT_DIR}/../config/patterns.conf}" ``` However, the scanner does not subsequently load or use `CONFIG_FILE`, and the referenced `config/patterns.conf` file is absent from the supplied project structure. The Python CLI accepts sensitivity controls: ```python parser.add_argument('--entropy-threshold', type=float, default=4.5, help='Shannon entropy threshold (default: 4.5)') parser.add_argument('--min-length', type=int, default=16, help='Minimum string length for entropy check (default: 16)') parser.add_argument('--version', action='version', version=f'%(prog)s {VERSION}') args = parser.parse_args() # Read input text = sys.stdin.read() # Initialize guard guard = OutputGuard() guard.scan(text) ``` The parsed `args.entropy_threshold` and `args.min_length` values are never passed to `scan` or `detect_high_entropy_strings`. ### Technical Analysis The documentation presents custom patterns and entropy thresholds as enforceable security controls. In practice: - The Bash configuration path is assigned but never read. - The documented configuration file is missing. - Python accepts entropy-related arguments but continues using hardcoded defaults. This creates silent configuration failure. Operators receive no error indicating that their custom credential patterns or adjusted detection thresholds are ineffective. ### Attack Path 1. An operator adds a custom secret pattern to the documented configuration path or sets `ARC_SHIELD_CONFIG`. 2. Alternatively, the operator invokes Python with a lower entropy threshold or different minimum length. 3. The implementation ignores the supplied configuration. 4. A secret that should have matched ...[truncated 428 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement loading and validation of `CONFIG_FILE`. - Include a default `config/patterns.conf` if the configuration model remains documented. - Fail closed, or at minimum issue a clear fatal error, when an explicitly supplied security configuration cannot be loaded. - Validate pattern severity, names, and regular expressions before scanning. - Pass CLI values into entropy analysis, for example: ```python guard.scan( text, entropy_threshold=args.entropy_threshold, min_length=args.min_length, ) ``` - Update method signatures so the configured values reach `detect_high_entropy_strings`. - Add tests proving that custom patterns, threshold changes, and minimum-length changes alter actual detection behavior. - Remove documentation for configuration options that are not implemented. ]]>
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
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (15)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Uninstall

```bash
rm -rf ~/.openclaw/workspace/skills/arc-shield
```

That's it. No system changes, no dependencies to clean up.
Confidence
90% confidence
Finding
The uninstall command uses `rm -rf` on a user-controlled filesystem path. Even though the path is specific to the skill directory, recursive forced deletion is dangerous because path typos, variable expansion surprises, or copy/paste mistakes can lead to unintended file removal.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Uninstall

```bash
rm -rf ~/.openclaw/workspace/skills/arc-shield
```

That's it. No system changes, no dependencies to clean up.
Confidence
90% confidence
Finding
The uninstall command uses `rm -rf` on a user-controlled filesystem path. Even though the path is specific to the skill directory, recursive forced deletion is dangerous because path typos, variable expansion surprises, or copy/paste mistakes can lead to unintended file removal.

Intent-Code Divergence

High
Confidence
97% confidence
Finding
This is a true vulnerability because the implementation contradicts the security promises in the header and help text. The script can disclose secrets twice: by passing raw content through stdout and by echoing matched secret fragments in warning output to stderr, which may be logged or exposed in terminals and CI systems. Misleading security tooling increases operational risk because users may rely on it as a protection boundary when it is not one.

Credential Access

High
Category
Privilege Escalation
Content
# Report findings
    report_finding "CRITICAL" "1Password Service Account Token" "$findings_1pass"
    report_finding "CRITICAL" "GitHub Personal Access Token" "$findings_github"
    report_finding "CRITICAL" "OpenAI API Key" "$findings_openai"
    report_finding "CRITICAL" "Stripe API Key" "$findings_stripe"
    report_finding "CRITICAL" "AWS Access Key" "$findings_aws"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This is a true vulnerability. In both default mode and strict mode, the script prints the original input to stdout, so any detected secret is still leaked downstream even though the tool presents itself as a sanitization barrier. In strict mode this is especially dangerous because the message is emitted before the block decision, defeating the core security claim of preventing accidental secret disclosure.

Missing User Warnings

High
Confidence
99% confidence
Finding
This is a true vulnerability. Strict mode is expected to prevent disclosure, but it emits the original sensitive content before checking FOUND_CRITICAL and exiting with a block message, so pipelines, logs, or downstream consumers can still receive the secret. The missing warning in help text worsens the issue because users are likely to trust strict mode for enforcement.

Intent-Code Divergence

High
Confidence
97% confidence
Finding
The comment/help text claims strict mode will block on critical findings, but the implementation later emits the message before blocking. This mismatch is dangerous because operators will rely on the documented security guarantee and may deploy the tool in data-loss-prevention paths where the secret has already escaped by the time the block occurs.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This is a true vulnerability: in --strict mode the program prints the original text to stdout before checking whether critical secrets were found and exiting with an error. That defeats the advertised blocking behavior and can leak secrets into logs, pipes, terminals, or downstream tools even when the tool reports the message as blocked.

Missing User Warnings

High
Confidence
99% confidence
Finding
This is a true vulnerability because the tool presents strict mode as a safety control, yet still sends unsanitized content to stdout before signaling failure. In the context of an output-sanitization skill whose purpose is to prevent secret leakage, this makes the issue more dangerous because users are especially likely to trust it as a last-line guard and route sensitive model output through it.

Credential Access

High
Category
Privilege Escalation
Content
# === WARN ===

[DETECT:SECRET_PATH] Check the file at ~/.secrets/instagram-password.txt

[DETECT:SECRET_PATH] Found in /home/user/.config/tokens/github-pat.json
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
# === WARN ===

[DETECT:SECRET_PATH] Check the file at ~/.secrets/instagram-password.txt

[DETECT:SECRET_PATH] Found in /home/user/.config/tokens/github-pat.json
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
# === WARN ===

[DETECT:SECRET_PATH] Check the file at ~/.secrets/instagram-password.txt

[DETECT:SECRET_PATH] Found in /home/user/.config/tokens/github-pat.json
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Session Persistence

Medium
Category
Rogue Agent
Content
If not using git:

```bash
# Create directory
mkdir -p ~/.openclaw/workspace/skills/arc-shield/{scripts,config,tests,examples}

# Download files (or copy manually)
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.

Session Persistence

Medium
Category
Rogue Agent
Content
### Method 2: Wrapper Script

Create a `send-safe` command:

```bash
# ~/.openclaw/bin/send-safe
Confidence
84% confidence
Finding
The wrapper-script instructions direct the user to place an executable in `~/.openclaw/bin/send-safe`, which creates a persistent command in the user's environment. Although intended as a safety integration, this is a form of session persistence because it installs durable behavior that may continue intercepting or influencing message sending after the initial setup.

Missing User Warnings

Low
Confidence
94% confidence
Finding
The uninstall section instructs users to run a recursive deletion command but does not explicitly warn that the action is irreversible and will permanently remove the skill directory. While the target path is fairly specific, documentation that normalizes `rm -rf` without a warning increases the risk of accidental data loss if the path is edited, expanded incorrectly, or copied carelessly.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
tests/test-samples.txt:14