Sanitizes Fortinet/FortiManager JSON-RPC and API responses before they are committed, used in tests, shared in issues, or pasted into AI tools.
The sanitizer keeps JSON structure and keys intact while redacting secrets and pseudonymizing identifiers. The goal is not to destroy all meaning. Test fixtures should still show useful relationships such as devices, ADOMs, VDOMs, policies, VLANs, SSIDs, HA clusters, platform types, status fields, and FortiManager response envelopes.
This is a Vibecode project: it was built iteratively with AI assistance and reviewed through tests and FortiManager-oriented sample data. Treat it as a practical helper, not as a certified security product.
Important:
- Always review sanitized output manually before publishing or committing it.
- This tool reduces leak risk, but it does not replace a final secret scan.
- Before publishing, also use tools such as
gitleaks,trufflehog, ordetect-secrets. - This project is not affiliated with, endorsed by, or supported by Fortinet.
- Fortinet and FortiManager are trademarks of their respective owners.
This project is currently tuned for FortiManager 7.6.x style JSON data.
It has been developed against:
- FortiManager 7.6.0 API Best Practices guidance
- FortiManager 7.6.7 FNDN-style schema references for
pm/config/firewall - FortiManager 7.6.7 FNDN-style schema references for
pm/config/switch-controller - FortiManager 7.6.7 FNDN-style schema references for
pm/config/wireless-controller - real-world-looking FortiManager JSON-RPC inventory fixtures under
dvmdb
Other FortiManager versions may add, rename, or remove fields. If you use this with another version, check the sanitized output carefully and add tests for new fields before sharing data publicly.
Use this when you have real FortiManager API responses and want to create safe test fixtures.
Good use cases:
- sharing FortiManager JSON-RPC responses in a GitHub issue
- building parser fixtures without leaking customer data
- preparing data for AI-assisted development
- creating test cases for FortiManager-to-NetBox or inventory adapters
Not ideal for:
- arbitrary non-JSON logs
- binary exports
- huge GB-scale JSON files, because the sanitizer loads JSON fully into memory
- replacing a formal data-loss-prevention or security-review process
Use Python 3.11 or newer. There are no runtime dependencies.
Put raw API responses in data/raw/:
data/raw/fmg-device-response.json
Sanitize one file:
python scripts/sanitize/sanitize_json.py data/raw/fmg-device-response.json data/sanitized/fmg-device-response.sanitized.jsonSanitize a whole directory:
python scripts/sanitize/sanitize_json.py data/raw/ data/sanitized/Directory mode automatically writes .sanitized.json filenames. For example:
data/raw/fmg-addresses.json
data/sanitized/fmg-addresses.sanitized.json
The default profile is balanced:
python scripts/sanitize/sanitize_json.py data/raw/ data/sanitized/ --profile balanced- Save original API responses under
data/raw/. - Run the sanitizer into
data/sanitized/. - Open the sanitized output and review it manually.
- Move stable, useful local fixtures to
data/fixtures/if needed. - Run a secret scanner before publishing any sample data.
- Commit the tool and documentation, not your local data files.
All files under data/raw/, data/sanitized/, and data/fixtures/ are ignored by Git except for .gitkeep. This is intentional for public repositories: even sanitized files should be reviewed manually before they are shared.
When sanitizer rules change, regenerate sanitized files from data/raw/ whenever possible. Re-sanitizing an already sanitized file is safe, but it cannot restore values that older rules already anonymized.
Use profiles to choose the privacy/semantics tradeoff:
| Profile | Use case | Behavior |
|---|---|---|
balanced |
Default for Git, tests, and AI context | Redacts secrets, pseudonymizes identifiers, preserves FortiManager semantics |
dev-fixture |
Parser and adapter fixtures | Same conservative behavior as balanced, intended for semantically rich test data |
strict |
Public sharing | More aggressive anonymization, including generic names such as root |
These values are usually preserved because they carry important parser or adapter semantics:
- FortiManager JSON-RPC structure:
cid,id,result,data,status,method,params,url,verbose - status and inventory numbers:
ha_mode,conn_status,dev_status,conf_status,fsw_cnt,fap_cnt,fex_cnt - firmware and platform signals:
platform_str,platform_id,os_ver,mr,build - generic FortiManager names in
balancedanddev-fixture:root,global,default,all - interface-like names such as
port1,wan2,lan,mgmt, andha - built-in policy references such as
all,always,HTTP, and common service names - netmasks such as
255.255.255.0insubnet,netmask, andmaskfields - policy semantics such as
policyid,action,schedule, andvlanid
These values are replaced with fixed placeholders because keeping relationships between them is not useful:
sessionandsession_id- API keys and Bearer tokens
- login data such as
user,username,adm_usr,passwd,password, andadm_pass - private keys and other secrets
- embedded credentials in URLs, for example
https://user:pass@example.com
These values are mapped deterministically inside one sanitizer run, so relationships remain visible without exposing real identifiers:
- ADOM names:
ADOM-001,ADOM-002 - device names:
DEVICE-001,DEVICE-002 - VDOM names:
VDOM-001,VDOM-002 - firewall address object names and references:
ADDR-001,ADDR-002 - firewall policy names:
POLICY-001,POLICY-002 - custom service references:
SERVICE-001,SERVICE-002 - custom interface references:
INTF-001,INTF-002 - switch identifiers and switch names:
SWITCH-001,SWITCH-002 - VLAN object names:
VLAN-001,VLAN-002 - wireless SSIDs and wireless profile names:
SSID-001,SSID-002 - hostnames:
HOST-001,HOST-002 - IP addresses and MAC addresses
- serial numbers under keys such as
serial,sn,serial_number, ortunnel_sn - HA cluster names under
ha_group_name - location coordinates under
latitudeandlongitude - e-mail addresses
- embedded JSON API log strings
Fortinet Developer Network schema files are useful as references for sanitizer policy, but they should not be committed unless their license permits it. The current FortiManager config policy is derived from common pm/config areas:
firewall/addressand policy references such assrcaddranddstaddrfirewall/policyfields such aspolicyid,srcintf,dstintf,service,schedule, andactionswitch-controllerfields such asswitch-id,fortilink,vlan, andvlanidwireless-controllerfields such asssid,vaps, andmax-clients
The goal is to keep parser-relevant structure visible while replacing organization-specific object names.
Before:
{
"method": "exec",
"params": [
{
"url": "/sys/login/user",
"data": {
"user": "admin",
"passwd": "SuperSecret123!"
}
}
],
"session": "abcdef1234567890",
"id": 1
}After:
{
"method": "exec",
"params": [
{
"url": "/sys/login/user",
"data": {
"user": "<REDACTED_USER>",
"passwd": "<REDACTED_SECRET>"
}
}
],
"session": "<REDACTED_SESSION>",
"id": 1
}Install pytest if needed, then run:
pytestBefore making a repository public:
- choose and add a license
- review every file in
data/sanitized/anddata/fixtures/ - keep
data/raw/uncommitted - keep local sanitized/fixture JSON files uncommitted unless you have manually approved them for publication
- run the test suite
- run a secret scanner
- add version-specific tests when supporting new FortiManager versions