fix(bitdefender): errorlog rule-compliance + moveCustomGroup param + ASCII-clean code
Finalizing the skill to "done, no errors, all skill rules": - errorlog compliance: gz.py no longer logs EXPECTED API responses (validation, method-not-found, not-configured, rate-limit, expected state) or `raw`/selftest runs to errorlog.md. Per CLAUDE.md "do not log expected/handled conditions". Verified: selftest + probes leave errorlog unchanged. - moveCustomGroup: param is `parentId`, not `newParentId` (6th doc-vs-live fix caught by a full param-shape audit). - ASCII-clean code: removed all non-ASCII (em-dashes, U+21D2 arrow) from scripts (avoids cp1252 encode errors; aligns with the ASCII-markers rule). - api-reference updated. Verified: 18/18 read commands rc=0 live; selftest 75/75; parser builds; ASCII markers + vault load + errorlog helper present. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""CLI for the bitdefender skill — GravityZone Cloud Public API.
|
||||
"""CLI for the bitdefender skill - GravityZone Cloud Public API.
|
||||
|
||||
Read-only subcommands run freely. Destructive subcommands (delete-endpoint,
|
||||
delete-package, delete-group) refuse to run unless --confirm is passed; without
|
||||
@@ -62,6 +62,45 @@ def _log_skill_error(skill, msg, context=""):
|
||||
pass
|
||||
|
||||
|
||||
# Substrings that mark an error as an EXPECTED API response (validation, probing,
|
||||
# not-configured, transient rate-limit, expected state) rather than a genuine skill
|
||||
# failure. The errorlog rule (CLAUDE.md) forbids logging expected/handled
|
||||
# conditions - only real failures worth pattern-spotting. These are NOT logged.
|
||||
_EXPECTED_ERROR_MARKERS = (
|
||||
"required parameter is missing",
|
||||
"invalid value",
|
||||
"not expected",
|
||||
"method not found",
|
||||
"is not available",
|
||||
"not available yet",
|
||||
"were not set",
|
||||
"not configured",
|
||||
"should not be used with",
|
||||
"you must specify a value",
|
||||
"cannot be restored from isolation",
|
||||
"429",
|
||||
"too many requests",
|
||||
)
|
||||
|
||||
|
||||
def _is_expected_error(msg: str) -> bool:
|
||||
m = (msg or "").lower()
|
||||
return any(marker in m for marker in _EXPECTED_ERROR_MARKERS)
|
||||
|
||||
|
||||
def _should_log_error(command: str, msg: str) -> bool:
|
||||
"""Decide whether a GravityZoneError is a genuine skill failure worth logging.
|
||||
|
||||
Skips: explicit suppression (selftest/probes set GZ_SUPPRESS_ERRORLOG), the
|
||||
exploratory `raw` subcommand, and expected API validation/probe responses.
|
||||
"""
|
||||
if os.environ.get("GZ_SUPPRESS_ERRORLOG"):
|
||||
return False
|
||||
if command == "raw":
|
||||
return False
|
||||
return not _is_expected_error(msg)
|
||||
|
||||
|
||||
def _emit(obj, as_json: bool, table_fn=None) -> None:
|
||||
if as_json or table_fn is None:
|
||||
print(json.dumps(obj, indent=2, default=_json_default))
|
||||
@@ -676,7 +715,7 @@ def cmd_make_group(client, args):
|
||||
# Substrings that mark a JSON-RPC method as state-destroying. `raw` can reach
|
||||
# any method (incl. UNVERIFIED ones), so gate these behind --confirm too.
|
||||
# isolate / blocklist add+remove are NEW destructive verbs from the incidents
|
||||
# (EDR) module — gate them in `raw` as well as via the dedicated subcommands.
|
||||
# (EDR) module - gate them in `raw` as well as via the dedicated subcommands.
|
||||
DESTRUCTIVE_RAW_PATTERNS = ("delete", "createuninstall", "createremove",
|
||||
"createreconfigure", "isolat", "addtoblocklist",
|
||||
"removefromblocklist", "assignpolicy",
|
||||
@@ -1019,7 +1058,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
help="Remove one blocklist entry (gated).",
|
||||
parents=[common])
|
||||
sp.add_argument("--id", required=True,
|
||||
help="hashItemId — the 'id' from `blocklist` output.")
|
||||
help="hashItemId - the 'id' from `blocklist` output.")
|
||||
sp.add_argument("--confirm", action="store_true")
|
||||
|
||||
sp = sub.add_parser("assign-policy",
|
||||
@@ -1208,8 +1247,9 @@ def main(argv=None) -> int:
|
||||
return rc if isinstance(rc, int) else 0
|
||||
except GravityZoneError as exc:
|
||||
print(f"[ERROR] {exc}", file=sys.stderr)
|
||||
_log_skill_error("bitdefender", f"{exc}",
|
||||
context=f"cmd={getattr(args, 'command', '?')}")
|
||||
cmd = getattr(args, "command", "?")
|
||||
if _should_log_error(cmd, str(exc)):
|
||||
_log_skill_error("bitdefender", f"{exc}", context=f"cmd={cmd}")
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
return 130
|
||||
|
||||
Reference in New Issue
Block a user