Sanitize PAN-OS XML config/snapshot files so they can be safely shared with others or with AI.
Project description
panscrub
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) — 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
# 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>) becomesdevice-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
pip install panscrub # once published
# or, from source:
pip install -e ".[dev]"
Usage
# 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file panscrub-0.2.0.tar.gz.
File metadata
- Download URL: panscrub-0.2.0.tar.gz
- Upload date:
- Size: 25.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4b2b1341b50b221a6bd497c610dbcb5765560bc64b5b6acd641c5b145be3efe6
|
|
| MD5 |
7e76ea3f6a8f8dd6d08f4f91d20f1864
|
|
| BLAKE2b-256 |
5439a1538344cf02a7e32535c12f652f381ff06fcd0a2c37d82c876d74feadc3
|
Provenance
The following attestation bundles were made for panscrub-0.2.0.tar.gz:
Publisher:
publish.yml on sknnr-chad/panscrub
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
panscrub-0.2.0.tar.gz -
Subject digest:
4b2b1341b50b221a6bd497c610dbcb5765560bc64b5b6acd641c5b145be3efe6 - Sigstore transparency entry: 2146365751
- Sigstore integration time:
-
Permalink:
sknnr-chad/panscrub@474bd546e87b11dea06d53b19b7d39a42d01af1c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/sknnr-chad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@474bd546e87b11dea06d53b19b7d39a42d01af1c -
Trigger Event:
release
-
Statement type:
File details
Details for the file panscrub-0.2.0-py3-none-any.whl.
File metadata
- Download URL: panscrub-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa0b400fdffe51d335dbddc60aee612b1b3120a880b96db46c1604f91585c9e8
|
|
| MD5 |
15ddbd667119c4b30945be88320d885b
|
|
| BLAKE2b-256 |
0653ba8b89fddbfa883f9b26f0b04829995e18f67bc680a3e2903c50afbdd7b6
|
Provenance
The following attestation bundles were made for panscrub-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on sknnr-chad/panscrub
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
panscrub-0.2.0-py3-none-any.whl -
Subject digest:
aa0b400fdffe51d335dbddc60aee612b1b3120a880b96db46c1604f91585c9e8 - Sigstore transparency entry: 2146365811
- Sigstore integration time:
-
Permalink:
sknnr-chad/panscrub@474bd546e87b11dea06d53b19b7d39a42d01af1c -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/sknnr-chad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@474bd546e87b11dea06d53b19b7d39a42d01af1c -
Trigger Event:
release
-
Statement type: