---
title: "Find the PII in a Folder of Files Before You Share or Migrate It"
description: "Scan a folder for PII with Phinder before you share or migrate it, and get a risk-ranked report of which files hold SSNs, email addresses, and other PII."
url: "https://philterd.ai/guides/find-pii-in-a-folder-with-phinder/"
---

[Knowledge base](https://philterd.ai/guides/) [How-to](https://philterd.ai/guides/?kind=how-to)

# Find the PII in a Folder of Files Before You Share or Migrate It

Scan a folder for PII with Phinder before you share or migrate it, and get a risk-ranked report of which files hold SSNs, email addresses, and other PII.

For [Phinder](https://philterd.ai/support/phinder/)

Before a file share gets copied to new storage, handed to a vendor, or opened up to a wider group, someone should know which files in it hold personal data. [Phinder](https://philterd.ai/phinder/) answers that. You point it at a folder, it reads every supported file, and it writes a report listing which kinds of PII it detected in which files, ranked by how sensitive they are. It does not change any file.

This guide walks through one scan of a folder tree, how to read the report, and how to turn the results into a starting redaction policy.

Detection is probabilistic. Phinder can miss PII and can flag text that is not PII, so treat the report as a map to check against your own data, not a clearance.

## 1\. Build Phinder

Phinder is a Java command-line application. It needs Java 25 or later and Maven to build:

```bash
git clone https://github.com/philterd/phinder.git
cd phinder
mvn clean install
```

The runnable JAR is `target/phinder-1.0.0-SNAPSHOT.jar`. The examples below call it `phinder.jar`.

If the folder contains images (`.png`, `.jpg`, `.jpeg`) or scanned PDFs, install Tesseract OCR as well. Without it, those files are reported as not scanned rather than silently skipped. If Tesseract cannot find its language data, set `TESSDATA_PREFIX` to the directory that holds files such as `eng.traineddata`.

## 2\. Check what it will read

Phinder picks a reader for each file by detecting its type, and it scans the file name as well as the contents. It reads:

- plain text and Markdown (`.txt`, `.md`)
- PDF, Word, and RTF (`.pdf`, `.docx`, `.doc`, `.rtf`)
- Excel and CSV (`.xlsx`, `.xls`, `.csv`)
- PowerPoint (`.pptx`, `.ppt`)
- email (`.eml`, `.msg`)
- log files (`.log`), read line by line
- images (`.png`, `.jpg`, `.jpeg`), through OCR

Anything else, such as a ZIP archive or a database file, is listed in the report as an unsupported file type. On PDFs, OCR is decided page by page: a page that yields fewer than 10 characters of text and contains an image is sent to OCR. A scanned page that also has a short text stamp, such as a Bates number, is read only as far as that text.

## 3\. Run the scan

Phinder writes `report.html` and `report.json` to the current directory and overwrites any report already there. Run it from a directory set aside for this scan, not from inside the share:

```bash
mkdir -p ~/scans/finance-share
cd ~/scans/finance-share
java -jar /opt/phinder/phinder.jar -i /mnt/finance-share -R > console.txt 2> errors.txt
```

`-i` takes a file or directory and can be repeated. Without `-R`, Phinder scans only the files directly inside the directory. With `-R` it walks every subdirectory. It does not follow symbolic links into other directories.

The console output prints each detected value with its type and confidence. That is useful at a terminal and a problem in a shared CI log, which is why the command above sends it to a file you can delete. The reports themselves record types and counts per file, not the values.

With no `-p` option, Phinder uses a built-in policy that looks for SSNs, credit card numbers, bank routing and IBAN numbers, passport and driver’s license numbers, email addresses, phone numbers, street addresses, first names and surnames, dates, ages, ZIP codes, IP and MAC addresses, URLs, VINs, and several other types. It does not include the PhEye person-name model. To change what it looks for, pass a Phileas policy with `-p policy.json`, the same JSON format Philter uses.

## 4\. Rank the results

On a large share, the per-file listing gets long. Three options control it:

```bash
java -jar /opt/phinder/phinder.jar -i /mnt/finance-share -R \
  --sort-by risk --min-risk 50 --top 10
```

- `--sort-by` orders the per-file sections by `risk` (the default), `magnitude`, `density`, or `name`.
- `--min-risk` leaves out files scoring below the given Risk Score. The default is `0`. The aggregate totals still cover every file scanned.
- `--top` sets how many files appear in the HTML report’s “Best Candidates for Redaction Testing” table. The default is `20`.

## Reading the report

Open `report.html` first. If any file could not be scanned, the report lists it at the top with a reason, such as an unsupported type, an unreadable file, or an OCR failure. Those files are in no score or count, so they may hold PII the report does not show. `--min-risk` never hides them, and Phinder exits with code 1 whenever anything failed, which makes the scan easy to gate in a script.

Each file gets three scores:

- The Risk Score measures sensitivity. It multiplies a per-type severity by the number of findings and by the detector’s confidence, and maps the total to a level from `NONE` through `LOW`, `MEDIUM`, and `HIGH` to `CRITICAL`.
- The Magnitude Score measures volume: findings weighted by type, with every weight 1.0 unless you pass `-w weights.json`.
- The Density Score is the Magnitude Score divided by the file’s word count.

Severities can be overridden with `--severities severities.json`, a JSON object of entity type to number, such as `{"email-address": 8.0}`.

`report.json` carries the same data for scripts. A per-file entry looks like this (illustrative values):

```json
"/mnt/finance-share/payroll/2025-q3-adjustments.xlsx": {
  "riskScore": 33.25,
  "riskLevel": "MEDIUM",
  "magnitudeScore": 5.0,
  "densityScore": 0.004,
  "counts": {
    "ssn": 2,
    "email-address": 3
  }
}
```

To list the critical files and the ones that failed:

```bash
jq -r '.perFileDetails | to_entries[] | select(.value.riskLevel == "CRITICAL") | .key' report.json
jq -r '.failedFiles | to_entries[] | "\(.key)\t\(.value)"' report.json
```

The top level also has `aggregateRiskScore`, `aggregateRiskLevel`, `aggregateCounts`, `filesScanned`, `filesReported`, and `filesFailed`.

## What to do next

Open the highest-ranked files and confirm what Phinder found. Then decide per file: move it out of scope, restrict access, or redact it before the share or migration goes ahead.

For redaction, Phinder can write a starter policy from the scan:

```bash
java -jar /opt/phinder/phinder.jar -i /mnt/finance-share -R --emit-policy starter-policy.json
```

It enables each entity type the scan found with a `REDACT` strategy, in the policy format [Philter](https://philterd.ai/philter/) and [Phileas](https://philterd.ai/phileas/) load. Give the file a `.phisql` extension to get [PhiSQL](https://github.com/philterd/phisql) instead. Tune it (see [Writing your first redaction policy](https://philterd.ai/guides/writing-your-first-redaction-policy/)) and measure it on a sample of your own documents with [Philter Scope](https://philterd.ai/philter-scope/) before you rely on it.

If the share keeps changing while you clean it up, the scan log can skip files whose content has not changed since the last run. It needs a MongoDB URI:

```bash
java -jar /opt/phinder/phinder.jar -i /mnt/finance-share -R \
  --mongodb "mongodb://localhost:27017" --skip-unchanged
```

Files that failed are not recorded as scanned, so they are retried on the next run.

## Related reading

- [Phinder: The open source scanner that maps where PII lives in your files.](https://philterd.ai/phinder/)
- [Phinder support: Guides, documentation, and the issue tracker.](https://philterd.ai/support/phinder/)
- [Phinder documentation: Every option, the report format, and how risk scores are calculated.](https://philterd.github.io/phinder/)
