What the Action does

The pompelmi GitHub Action scans your repository (or a subdirectory) for viruses and malware using ClamAV. Everything runs inside Docker on the GitHub Actions runner — no external services, no API keys, no data egress.

The Action performs these steps in order:

  1. Builds a Docker image based on node:20-slim with ClamAV installed.
  2. Runs freshclam to download the latest virus definitions.
  3. Resolves the scan path (relative to the workspace root).
  4. Calls scanDirectory(path) for directories, or scan(path) for single files.
  5. Writes the status and infected-files step outputs.
  6. Posts a formatted table to $GITHUB_STEP_SUMMARY so results appear in the workflow summary UI.
  7. Exits with code 1 if any infected file is found and fail-on-virus is 'true'.

Minimal usage

Add these two steps to any workflow. The Action scans the entire workspace and fails the job if any malware is detected.

steps:
  - uses: actions/checkout@v4
  - uses: pompelmi/pompelmi@v1.8.0
No configuration needed for a basic repository scan. path defaults to . (the workspace root), and fail-on-virus defaults to 'true'.

Inputs

Input Default Description
path '.' Path to scan, relative to the workspace root. Can be a file or directory.
fail-on-virus 'true' Set to 'false' to let the job continue even when a virus is found. Use the status output to handle the result yourself.
comment-on-pr 'true' Post a comment to the pull request with scan results. Requires GITHUB_TOKEN to be available. Only runs on pull_request events.

Outputs

Output Values Description
status 'clean' | 'infected' Overall scan result.
infected-files newline-separated paths, or empty string Paths of all infected files found during the scan. Empty when status is 'clean'.

Reference outputs in subsequent steps as ${{ steps.<id>.outputs.status }} and ${{ steps.<id>.outputs.infected-files }}.

Example: scan on every PR

This workflow scans the repository on every push to main and on every pull request. The id field lets you reference the outputs.

name: Virus scan

on:
  push:
    branches: [main]
  pull_request:

jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write   # needed for comment-on-pr

    steps:
      - uses: actions/checkout@v4

      - name: Scan repository
        id: pompelmi
        uses: pompelmi/pompelmi@v1.8.0
        with:
          comment-on-pr: 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Print result
        run: echo "Scan status: ${{ steps.pompelmi.outputs.status }}"
A working example workflow is available in the repository at .github/workflows/action-example.yml.

Example: scan only uploads/

Set fail-on-virus: 'false' to inspect results yourself and take custom action (e.g. notify a Slack channel or delete only the infected files).

steps:
  - uses: actions/checkout@v4

  - name: Scan uploads directory
    id: scan
    uses: pompelmi/pompelmi@v1.8.0
    with:
      path: 'uploads'
      fail-on-virus: 'false'

  - name: Handle infected files
    if: steps.scan.outputs.status == 'infected'
    run: |
      echo "Infected files found:"
      echo "${{ steps.scan.outputs.infected-files }}"
      # add your remediation logic here
      exit 1

Job summary

After each scan the Action writes a formatted table to $GITHUB_STEP_SUMMARY. The table appears on the workflow run page in the GitHub UI under the Summary tab.

## pompelmi scan results

| File | Status |
|------|--------|
| src/index.js | Clean |
| uploads/report.pdf | Clean |

**Overall: clean** — 2 files scanned, 0 infected.

When infected files are detected, the table highlights them and the overall status row reads infected in red.

Layer caching

The Action builds a Docker image on every run. GitHub Actions caches Docker layers automatically on ubuntu-latest runners.

Run type Typical duration Notes
First run (cold cache) 30–90 seconds Builds the image and downloads ClamAV definitions from scratch.
Subsequent runs (warm cache) 10–30 seconds Docker layers are restored from cache. Only changed layers are rebuilt.
freshclam always runs to fetch the latest definitions, even on warm cache runs. This adds a few seconds but ensures your signatures are current.

On self-hosted runners, the Docker layer cache persists on disk between runs (no eviction), so warm runs are consistently fast. On GitHub-hosted runners, the cache is shared across the repository and may be evicted after 7 days of inactivity.

Add this badge

Show users that your repository is scanned for malware by adding the pompelmi badge to your README:

[![Scanned by pompelmi](https://img.shields.io/badge/scanned%20by-pompelmi-orange)](https://github.com/pompelmi/pompelmi)

Renders as: Scanned by pompelmi

See BADGE.md in the repository root for additional badge variants (SVG data URI, dark/light theme versions) with full copy-paste snippets.