Metadata-Version: 2.4
Name: panscrub
Version: 0.2.0
Summary: Sanitize PAN-OS XML config/snapshot files so they can be safely shared with others or with AI.
Project-URL: Homepage, https://github.com/sknnr-chad/panscrub
Project-URL: Repository, https://github.com/sknnr-chad/panscrub
Project-URL: Issues, https://github.com/sknnr-chad/panscrub/issues
Author-email: Chad <chad@prspower.com>
License: MIT
License-File: LICENSE
Keywords: firewall,palo-alto,panos,redact,sanitize,security,xml
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security
Classifier: Topic :: System :: Networking :: Firewalls
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: lxml>=5.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# panscrub

[![CI](https://github.com/sknnr-chad/panscrub/actions/workflows/ci.yml/badge.svg)](https://github.com/sknnr-chad/panscrub/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/panscrub)](https://pypi.org/project/panscrub/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

Sanitize PAN-OS XML config / snapshot files so they can be safely shared with
colleagues, vendors, or an AI assistant — without leaking credentials, keys, or
your network topology.

> ⚠️ **Beta.** Validated against real PAN-OS snapshots, but no sanitizer can
> guarantee completeness against every config shape. Always eyeball the output
> before sharing, run `--check`, and please report any element panscrub misses
> (see [SECURITY.md](SECURITY.md)) — coverage is the whole point.

## Why

A PAN-OS config is a goldmine of secrets: admin password hashes (`phash`),
IPSec pre-shared keys, certificate private keys, SNMP community strings,
RADIUS/TACACS/LDAP secrets, API keys — plus your real IPs and hostnames. You
can't paste that into a chat window. panscrub strips it first.

## Two levels

| Level | What it does | Reversible? |
|-------|--------------|-------------|
| **1** (default) | Strips cryptographic material & credentials (keys, hashes, PSKs, secrets). No analytical value, so it's removed outright. | No — gone for good |
| **2** | Also anonymizes IPs, FQDNs, and the device hostname **consistently and subnet-preserving**, saving a mapping so you can translate AI advice back to your real network. | Yes — via mapping file |

If you only care about keeping keys and hashes out of an AI chat (the common
case), **Level 1 is all you need.**

### Level 2: structure-preserving anonymization

```bash
# Strip secrets AND anonymize IPs/FQDNs/hostnames. Saves a mapping sidecar.
panscrub config.xml --level 2
#   -> config.xml.sanitized.xml         (safe to share)
#   -> config.xml.panscrub-map.json     (SENSITIVE - keep private)

# Later, translate Claude's advice back to your real network:
panscrub advice.txt --restore --map config.xml.panscrub-map.json
```

- **IPs** use a prefix-preserving transform (Crypto-PAn style): any subnet
  relationship is retained — hosts in the same real /24 land in the same
  anonymized /24 — so topology stays analyzable. Special-use addresses
  (`0.0.0.0`, loopback, broadcast) are left intact because they carry meaning.
- **FQDNs** map to `exampleN.com`, with shared real domains mapped consistently
  so "these hosts are in the same domain" survives. Wildcards (`*.example.com`)
  and domains embedded in object names are handled.
- **The device hostname** (`<hostname>`/`<devicename>`) becomes `device-N`.
- The same mapping keys produce the **same** anonymized output on re-runs, so
  diffs across snapshots stay meaningful. Reuse the map file to keep it stable.

**The mapping file is as sensitive as the original config** — it reverses the
anonymization. panscrub gitignores `*.panscrub-map.json` by default; never share it.

**Known limitations (Level 2):** bare hostnames outside the `<hostname>` element
aren't anonymized (too ambiguous to match safely); usernames in `user@domain`
are left while the domain is anonymized; IPv6 is anonymized but less battle-tested
than IPv4. Always eyeball Level 2 output before sharing.

## Install

```bash
pip install panscrub        # once published
# or, from source:
pip install -e ".[dev]"
```

## Usage

```bash
# Strip secrets -> writes config.xml.sanitized.xml next to the source
panscrub config.xml

# Choose the output path
panscrub config.xml -o safe.xml

# Overwrite in place (careful)
panscrub config.xml --in-place

# Strip, then audit the output for anything secret-looking that slipped
# through (by content, regardless of tag name). Exits non-zero if so.
panscrub config.xml --check
```

### `--check`: the safety net

The sanitizer only removes secrets in elements it *knows about*. `--check`
scans the sanitized output for values that *look like* secrets anywhere —
PEM private-key blocks, unix password hashes, PAN-OS encrypted markers,
URL-embedded credentials (`scheme://user:pass@host`), and high-entropy base64
blobs — in element text, tail text, attribute values, and comments, so a
secret can't hide in a location the tag-driven strip doesn't model. It's
heuristic and errs toward flagging; public certificate bodies are exempted
from the entropy check to keep the signal clean. A misplaced *private* key is
still caught even in an exempt tag.

panscrub prints a redaction summary to stderr:

```
panscrub: redacted 6 secret element(s) -> config.xml.sanitized.xml
     2  password
     1  IKE/IPSec pre-shared key
     1  certificate private key
     ...
```

## How it works

panscrub parses the XML with `lxml` and matches **leaf element tag names**
against an auditable ruleset (`src/panscrub/rules.py`). PAN-OS uses stable tag
names for secret-bearing leaves regardless of nesting, which makes this both
robust and easy to review. The design is **fail-closed**: when a tag is known
to carry a secret, its contents are removed even if we can't anonymize them.

XML comments and processing instructions are dropped during parsing — they are
not config data and can themselves hide secrets (e.g. an admin note with a
password) that a tag-driven strip would never see.

As a second layer, Level 1 also strips PAN-OS field-encrypted values (the
distinctive `-AQ==` marker) **by content, wherever they appear** — element
text, tail text, or attribute values — so an encrypted secret in a tag the
ruleset doesn't recognize is still removed, not merely flagged. Credentials
embedded in URLs (`https://user:password@host/...`, as in EDL sources) have
their userinfo redacted while the host is kept for analysis. Every redaction
leaves the same `__PANSCRUB_REDACTED__` token, and panscrub warns loudly if a
run redacts **zero** secrets (usually the wrong file or a ruleset gap).

## Contributing

The ruleset is the heart of the project. If your config has a secret-bearing
element panscrub doesn't catch, that's a bug — please file it (with the element
name, not the secret!) or PR an addition to `LEVEL1_RULES`.

## License

MIT
