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>
169 lines
6.9 KiB
Markdown
169 lines
6.9 KiB
Markdown
---
|
|
name: bitdefender
|
|
description: >-
|
|
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`.
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
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`.
|