Files
claudetools/.claude/skills/bitdefender/SKILL.md
Mike Swanson e6056433a2 harness: fix py-vs-python3 doc gap — add py.sh resolver, repoint skill/command docs
The skill/command DOCS instructed Claude to run a bare `py ...`, which is the
Windows py-launcher — absent on Linux/macOS (exit 127, hit on GURU-KALI). A blind
py->python3 swap is wrong too: python3 is a broken MS Store shim on some Windows
boxes where `py` is the correct launcher.

Fix mirrors the resolution the .sh skill scripts already do:
- New .claude/scripts/py.sh: picks the interpreter that actually RUNS —
  identity.json python.command first, then py -> python3 -> python, each
  validated with `-c 'import sys'` so the MS Store stub is skipped. exec's it.
- Repointed all DOC invocations (10 files, ~70 sites) from `py ...` to
  `bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" ...` (incl. the `py -c` and
  `py -` heredoc forms in checkpoint.md / mailbox.md).
- Left the .sh skill scripts untouched — they already resolve py/python/python3.
- errorlog.md: marked the GURU-KALI entry RESOLVED.

Depends on CLAUDETOOLS_ROOT (seeded by ensure-settings-env.py); py.sh also
self-resolves the repo root via git/cwd as a fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 09:48:54 -07:00

6.9 KiB

name, description
name description
bitdefender Manage the Arizona Computer Guru (ACG) Bitdefender GravityZone Cloud MSP tenant via the Public JSON-RPC API. Inventory and audit endpoints, run live security sweeps (infected / outdated-signature / outdated-product), list client companies, build and fetch installation packages, manage custom groups, start scans, move/delete endpoints (gated), inspect policies (read-only, shallow), and review quarantine. Invoke for: "bitdefender", "gravityzone", "gravity zone", "add machine to bitdefender", "install bitdefender on", "list endpoints", "infected machines", "av coverage", "security sweep", "endpoint protection", "policy assignment", "quarantine". This skill talks to the real production ACG GravityZone partner tenant — treat destructive actions conservatively.

Bitdefender GravityZone Skill

Standalone CLI client for the GravityZone Cloud Public API (JSON-RPC). Talks to the live ACG partner tenant. Read-only by default; destructive operations are gated behind --confirm.

Running the CLI

This machine's Python launcher is py (per identity.json). The scripts also work with python/python3.

# from the scripts dir, or pass full paths
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" status
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" companies
bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" "C:/claudetools/.claude/skills/bitdefender/scripts/gz.py" sweep --company <id> --json

Transport auto-selects: uses httpx if installed, otherwise stdlib urllib (no third-party dependency required).

Credentials

The API key is NEVER hardcoded. At runtime the client loads it from the SOPS vault:

bash "$CLAUDETOOLS_ROOT/.claude/scripts/vault.sh" \
     get-field msp-tools/gravityzone.sops.yaml credentials.api_key

CLAUDETOOLS_ROOT resolves from the env var, else claudetools_root in C:/claudetools/.claude/identity.json, else C:/claudetools. For testing you can override with GRAVITYZONE_API_KEY. Auth is HTTP Basic (key as username, empty password).

Cache model (important)

The CLI keeps a local cache at .claude/skills/bitdefender/.cache/inventory.json (gitignored — no secrets, no PII).

  • Cached (identity / structure tier): company id<->name map, endpoint id<->name/company/fqdn map, policy id<->name map, package list, and custom groups created via this tool. TTL = 86400s (24h).
  • NEVER cached (volatile): infected status, last-seen, online/offline, signature/product freshness. Those are ALWAYS pulled live — sweep and endpoint always hit the API.
  • Refresh: inventory --refresh forces a full re-pull. get_inventory() auto-refreshes when the cache is stale.
  • Write-through: a successful create-package or make-group updates the cache with the new id immediately, so you don't need a full refresh to reference it.

Policy API limitation

The Public API exposes policies only shallowly. You CAN list policies, read their id/name, audit which endpoints carry which policy (via endpoint detail), and — via the UNVERIFIED assignPolicy — assign an existing policy. You CANNOT read the granular module configuration of a policy, and there is NO create / edit / clone policy method in the Public API. For policy authoring, use the GravityZone console.

Safety gating

Destructive subcommands refuse to run without --confirm; without it they print what they would do and exit non-zero:

  • delete-endpoint <id> --confirm
  • delete-package --package <name> --confirm
  • delete-group --group <id> --confirm
  • isolate --endpoints <id> ... --confirm (cuts the endpoint off the network; reversible via unisolate)
  • unisolate --endpoints <id> ... --confirm
  • blocklist-add --company <id> --hashes <h> ... --confirm
  • blocklist-remove --id <hashItemId> --confirm

Never run destructive calls casually against this tenant. UNVERIFIED methods (assignPolicy, uninstall/reconfigure tasks, quarantine remove/restore, set label) are intentionally NOT exposed as dedicated subcommands — reach them only through raw after confirming the correct params against references/api-reference.md and the official Bitdefender docs.

raw itself refuses destructive method names (delete/uninstall/remove/ reconfigure, plus the EDR verbs isolat*/addToBlocklist/removeFromBlocklist) unless --confirm is passed. Note that raw prints the upstream response verbatim — it can carry data from the called method, so do not paste raw output into tickets/logs without review.

Common commands

GZ="bash "$CLAUDETOOLS_ROOT/.claude/scripts/py.sh" C:/claudetools/.claude/skills/bitdefender/scripts/gz.py"

# Status / inventory
$GZ status
$GZ companies
$GZ inventory --refresh
$GZ endpoints --company <companyId>
$GZ endpoint <endpointId>

# Live security posture
$GZ sweep --company <companyId>          # readable table
$GZ sweep --company <companyId> --json   # machine output

# Policies (read-only, shallow)
$GZ policies
$GZ policy <policyId>

# Quarantine
$GZ quarantine --company <companyId>

# Deployment
$GZ packages
$GZ create-package --name "Win Default" --company <companyId>
$GZ install-links --package "Win Default" --company <companyId>

# Org structure
$GZ make-group --name "New Site" --parent <parentId>
$GZ move --endpoints <id1> <id2> --group <groupId>

# Scans
$GZ scan --targets <id1> <id2> --type 2 --name "Full scan"

# EDR / incident response
$GZ blocklist                                  # list blocklisted hashes (whole tenant)
$GZ blocklist --company <companyId>            # scope to one company
$GZ incidents --company <companyId>            # list incidents (parentId required; method UNVERIFIED on this tenant - may return "Method not found")
$GZ isolate --endpoints <id1> <id2> --confirm  # cut endpoint(s) off the network (reversible via unisolate)
$GZ unisolate --endpoints <id1> <id2> --confirm   # restore endpoint(s) from isolation
$GZ blocklist-add --company <companyId> --hashes <h1> <h2> --hash-type 1 --source-info "..." --confirm
$GZ blocklist-remove --id <hashItemId> --confirm  # id comes from `blocklist` output

# Power use — call any method directly
$GZ raw --module network --method getEndpointsList --params '{"page":1,"perPage":50}'

# Destructive (gated)
$GZ delete-endpoint <id> --confirm

Phase-2 hooks (not yet implemented)

  • GuruRMM push-deploy: use install-links to fetch the platform installer URL, then push the installer to a target via the GuruRMM agent fleet (/rmm) for one-step Bitdefender rollout from RMM.
  • Push webhook: subscribe to GravityZone Push events (new malware / endpoint state changes) and surface them through the coord API / RMM alerts instead of polling sweep.

Reference

Full verified vs unverified method spec, JSON-RPC envelope, auth, and the policy/deployment caveats: references/api-reference.md.