# How the scan-plugins Action Performs Policy Scanning in the Claude Community

> Discover how the scan-plugins GitHub Action uses Claude and structured prompts for automated policy scanning of external plugins against Anthropic safety guidelines.

- Repository: [Anthropic/claude-plugins-community](https://github.com/anthropics/claude-plugins-community)
- Tags: how-to-guide
- Published: 2026-08-24

---

**The scan-plugins GitHub Action performs automated policy scanning by orchestrating Claude to analyze changed external plugins against Anthropic's safety policies using a structured prompt and JSON schema validation.**

The `anthropics/claude-plugins-community` repository maintains a marketplace of community plugins, and the `scan-plugins` action ensures every external submission complies with security standards before merging. This action automates what would otherwise be a manual code review process, checking for credential exfiltration, unauthorized network calls, and other safety violations by leveraging the Claude CLI.

## Overview of the Policy Scanning Architecture

The policy scanning workflow operates as a **Claude-driven policy reviewer** that executes within GitHub Actions. According to the source code in [`.github/actions/scan-plugins/scripts/scan.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/actions/scan-plugins/scripts/scan.sh), the action follows a strict pipeline: authenticating with the Anthropic API, resolving which plugins changed in the current pull request, checking out each plugin at its exact commit hash, and invoking Claude with a detailed safety prompt.

The action relies on two critical policy definitions stored in the repository:
- **[`policy/prompt.md`](https://github.com/anthropics/claude-plugins-community/blob/main/policy/prompt.md)**: Human-readable instructions telling Claude what safety criteria to evaluate
- **[`policy/schema.json`](https://github.com/anthropics/claude-plugins-community/blob/main/policy/schema.json)**: Structural validation rules ensuring Claude returns parseable JSON with required fields like `passes`, `summary`, and `violations`

## Authentication and Environment Validation

Before scanning begins, the script validates the runtime environment at lines 7-19 of [`scan.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/scan.sh). It first checks for either an `ANTHROPIC_API_KEY` or a federation rule ID, explicitly unsetting empty variables to prevent the Claude CLI from sending blank authentication headers.

The action also asserts that required inputs are defined:
- `MARKETPLACE_PATH`: Path to the marketplace JSON file
- `BASE_REF`: Git reference for comparison to detect changes
- `ALLOWED_HOSTS`: Permitted domains for external plugin sources
- `SCAN_TIMEOUT_SECS`: Maximum duration for individual plugin analysis

```bash

# Environment validation from scan.sh

if [[ -z "${ANTHROPIC_API_KEY:-}" ]]; then
  unset ANTHROPIC_API_KEY  # Prevent empty header errors

fi

# Input validation ensures critical variables are populated

: "${MARKETPLACE_PATH:?}" "${BASE_REF:?}" "${ALLOWED_HOSTS:?}" "${SCAN_TIMEOUT_SECS:?}"

```

## Resolving Scan Targets

The action uses the shared library [`lib/targets.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/lib/targets.sh) to determine which plugins require scanning. The `resolve_scan_targets` function extracts `{name, source}` objects from the marketplace JSON and writes them to [`targets.json`](https://github.com/anthropics/claude-plugins-community/blob/main/targets.json).

Target resolution follows two modes controlled by the `SCAN_ALL_EXTERNAL` environment variable:
- **Full scan**: When `SCAN_ALL_EXTERNAL=true`, every external plugin entry is targeted
- **Incremental scan**: Otherwise, only plugins added or changed relative to the `BASE_REF` commit are included (lines 30-38 of [`scan.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/scan.sh))

```bash

# Target resolution logic

source "$(dirname "$0")/../lib/targets.sh"
resolve_scan_targets "$MARKETPLACE_PATH" "$BASE_REF" "$SCAN_ALL_EXTERNAL" > targets.json

```

## Plugin Checkout and Pre-Validation

For each entry in [`targets.json`](https://github.com/anthropics/claude-plugins-community/blob/main/targets.json), the script performs rigorous validation and checkout (lines 71-109 of [`scan.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/scan.sh)):

1. **URL Safety Check**: Validates the plugin source URL against the `ALLOWED_HOSTS` list, skipping unsafe entries with warnings
2. **SHA Verification**: Ensures the target commit hash is present and valid
3. **Subdirectory Handling**: Respects optional subdirectory specifications for monorepo-style plugins
4. **Precise Checkout**: Clones with `--depth 1`, fetches the exact SHA, and checks out the specific commit to prevent TOCTOU attacks

```bash

# Checkout process for each plugin

git clone --depth 1 "$URL" "$TMPDIR"
git -C "$TMPDIR" fetch --depth 1 origin "$SHA"
git -C "$TMPDIR" checkout "$SHA"

```

## Claude Policy Analysis Execution

Inside the checked-out plugin directory, the action invokes the Claude CLI with restricted tool access and structured output requirements (lines 111-122 of [`scan.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/scan.sh)):

```bash
claude -p "$PROMPT" \
       --bare \
       --allowed-tools "Read,Glob,Grep" \
       --output-format json \
       --json-schema "$SCHEMA"

```

**Key execution parameters:**
- **Prompt injection**: Loads [`policy/prompt.md`](https://github.com/anthropics/claude-plugins-community/blob/main/policy/prompt.md) which instructs Claude to examine plugin manifests, `skills/` directories, hidden files, and configuration for safety violations
- **Tool restrictions**: Limits Claude to read-only operations (`Read`, `Glob`, `Grep`) preventing any execution of untrusted code
- **Schema enforcement**: Uses [`policy/schema.json`](https://github.com/anthropics/claude-plugins-community/blob/main/policy/schema.json) to enforce output structure containing boolean `passes`, string `summary`, and array `violations`

The prompt specifically directs Claude to identify:
- External network calls outside allowed domains
- Credential exfiltration attempts
- Unauthorized file system access
- Dynamic code execution risks

## Result Parsing and Violation Handling

After Claude returns the analysis, the script parses the JSON using `jq` and extracts specific fields (lines 26-50 of [`scan.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/scan.sh)):
- `passes`: Boolean indicating overall safety
- `summary`: Human-readable assessment
- `violations`: Array of specific policy breaches
- `may_make_external_network_calls`: Boolean flag for network activity
- `may_download_additional_software`: Boolean flag for dynamic installation

If parsing fails, the action logs a warning but continues processing other plugins. Valid results are merged with static pin-check data (`unpinned_autoexec_*` flags) and appended to the `scanned` results array.

The **fail/warning logic** (lines 55-63) determines CI behavior:
- When a plugin has `passes: false`, its name is added to the `failed` list
- If `FAIL_ON_FINDINGS=true`, the action emits a GitHub error annotation and fails the workflow
- If `FAIL_ON_FINDINGS=false` (default), it emits a warning annotation allowing the workflow to continue

## Workflow Integration and Outputs

The action exports three workflow outputs for downstream consumption:
- `scanned`: JSON array of all analyzed plugins with their verdicts
- `failed`: Array of plugin names that failed policy checks
- `result`: Overall pass/fail status

It also generates a GitHub step summary in markdown format, displaying:
- Pass/fail icons for each plugin
- External network and software download flags
- Detailed violation descriptions

```yaml

# Example workflow integration

- name: Scan plugins for policy compliance
  uses: anthropics/claude-plugins-community/.github/actions/scan-plugins@v1
  with:
    marketplace_path: marketplace.json
    base_ref: ${{ github.event.before }}
    allowed_hosts: 'github.com gitlab.com'
    scan_timeout_secs: 300
    fail_on_findings: true

```

## Summary

- The **scan-plugins action** automates security review by invoking Claude CLI against changed external plugins in the marketplace.
- It validates the runtime environment and ensures the `ANTHROPIC_API_KEY` is properly configured before processing.
- Target resolution uses [`lib/targets.sh`](https://github.com/anthropics/claude-plugins-community/blob/main/lib/targets.sh) to identify either all external plugins or only those modified in the current pull request.
- Each plugin is checked out at its exact commit hash into a temporary directory for isolated analysis.
- Claude evaluates plugins using the **policy prompt** ([`policy/prompt.md`](https://github.com/anthropics/claude-plugins-community/blob/main/policy/prompt.md)) and returns structured JSON validated against **schema.json** ([`policy/schema.json`](https://github.com/anthropics/claude-plugins-community/blob/main/policy/schema.json)).
- Results are parsed with `jq` to extract `passes`, `violations`, and safety flags, optionally failing the CI run based on the `FAIL_ON_FINDINGS` setting.

## Frequently Asked Questions

### What triggers the scan-plugins action to run?

The action triggers on pull requests that modify the [`marketplace.json`](https://github.com/anthropics/claude-plugins-community/blob/main/marketplace.json) file or external plugin definitions. According to [`.github/workflows/validate-plugins.yml`](https://github.com/anthropics/claude-plugins-community/blob/main/.github/workflows/validate-plugins.yml), it executes as part of the plugin validation pipeline, comparing changes against the `BASE_REF` to determine which specific plugins require scanning.

### How does the action prevent Claude from executing malicious code during scanning?

The action restricts Claude's capabilities using the `--allowed-tools` flag limited to `Read`, `Glob`, and `Grep` only. This read-only configuration ensures Claude can analyze file contents but cannot execute any code within the checked-out plugin repositories.

### What happens if Claude returns invalid JSON or fails to parse the plugin?

If `jq` cannot parse Claude's output, the action logs a warning message identifying the specific plugin and continues processing the remaining targets. This prevents one malformed response from blocking the entire validation pipeline, though the individual plugin receives no verdict.

### Can the scan-plugins action be used with private plugin repositories?

Yes, provided the GitHub Actions runner has appropriate authentication credentials and the repository URLs are included in the `ALLOWED_HOSTS` list. The action performs standard `git clone` operations, so it respects the repository access permissions of the workflow environment.