From b681e0e03b8b4aa6217024c4bde5a246516776e9 Mon Sep 17 00:00:00 2001 From: sickn33 Date: Sat, 28 Mar 2026 17:37:29 +0100 Subject: [PATCH] fix(security): Harden skill security checks --- .../claude-monitor/scripts/api_bench.py | 13 +++++++- .../backend/package-lock.json | 6 ++-- .../todo-app-generated/backend/package.json | 1 + .../frontend/package-lock.json | 6 ++-- .../todo-app-generated/frontend/package.json | 1 + .../scripts/send_test_message.py | 32 +++++++++++-------- .../scripts/validate_config.py | 31 ++++++++---------- .../claude-monitor/scripts/api_bench.py | 13 +++++++- .../scripts/send_test_message.py | 32 +++++++++++-------- .../scripts/validate_config.py | 31 ++++++++---------- .../backend/package-lock.json | 6 ++-- .../todo-app-generated/backend/package.json | 1 + .../frontend/package-lock.json | 6 ++-- .../todo-app-generated/frontend/package.json | 1 + 14 files changed, 104 insertions(+), 76 deletions(-) diff --git a/plugins/antigravity-awesome-skills-claude/skills/claude-monitor/scripts/api_bench.py b/plugins/antigravity-awesome-skills-claude/skills/claude-monitor/scripts/api_bench.py index ce01cee4..f210af2e 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/claude-monitor/scripts/api_bench.py +++ b/plugins/antigravity-awesome-skills-claude/skills/claude-monitor/scripts/api_bench.py @@ -34,6 +34,17 @@ ENDPOINTS = [ ] +def create_tls_context(): + """Cria contexto TLS restringindo conexoes a TLS 1.2+.""" + context = ssl.create_default_context() + if hasattr(ssl, "TLSVersion"): + context.minimum_version = ssl.TLSVersion.TLSv1_2 + else: + context.options |= getattr(ssl, "OP_NO_TLSv1", 0) + context.options |= getattr(ssl, "OP_NO_TLSv1_1", 0) + return context + + def test_tcp_latency(host, port, timeout=5): """Testa latĂȘncia TCP para um host:port.""" try: @@ -49,7 +60,7 @@ def test_tcp_latency(host, port, timeout=5): def test_tls_handshake(host, port=443, timeout=5): """Testa tempo do handshake TLS.""" try: - context = ssl.create_default_context() + context = create_tls_context() start = time.time() with socket.create_connection((host, port), timeout=timeout) as sock: with context.wrap_socket(sock, server_hostname=host) as ssock: diff --git a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json index b4d6c0e2..64387bb9 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +++ b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json @@ -1110,9 +1110,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, "node_modules/prebuild-install": { diff --git a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package.json b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package.json index 4cec7d90..805ca3f8 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package.json +++ b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/backend/package.json @@ -24,6 +24,7 @@ }, "overrides": { "diff": "4.0.4", + "path-to-regexp": "0.1.13", "qs": "^6.15.0" } } diff --git a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json index 2a4b1d15..4d91dee8 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +++ b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json @@ -1527,9 +1527,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package.json b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package.json index 9b0fcdae..b860b5c2 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package.json +++ b/plugins/antigravity-awesome-skills-claude/skills/loki-mode/examples/todo-app-generated/frontend/package.json @@ -23,6 +23,7 @@ "react-dom": "^19.2.3" }, "overrides": { + "picomatch": "4.0.4", "rollup": "^4.59.0" } } diff --git a/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/send_test_message.py b/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/send_test_message.py index 7f121a1f..99196135 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/send_test_message.py +++ b/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/send_test_message.py @@ -28,11 +28,21 @@ except ImportError: GRAPH_API = "https://graph.facebook.com/v21.0" -def _mask_secret(value: str) -> str: - """Return a masked version of a secret for safe logging.""" - if not value or len(value) < 8: - return "***masked***" - return f"{value[:6]}...masked" +def _redact_json(value): + """Recursively redact common secret-bearing keys before logging JSON.""" + sensitive_keys = {"authorization", "token", "access_token", "app_secret", "secret"} + + if isinstance(value, dict): + redacted = {} + for key, item in value.items(): + if key.lower() in sensitive_keys: + redacted[key] = "***redacted***" + else: + redacted[key] = _redact_json(item) + return redacted + if isinstance(value, list): + return [_redact_json(item) for item in value] + return value def send_test(to: str, message: str) -> None: @@ -84,11 +94,7 @@ def send_test(to: str, message: str) -> None: print() print("Full response:") - # Mask token in response output to prevent credential leakage - response_str = json.dumps(data, indent=2) - if token and token in response_str: - response_str = response_str.replace(token, _mask_secret(token)) - print(response_str) + print(json.dumps(_redact_json(data), indent=2)) except httpx.ConnectError: print("Error: Connection failed. Check your internet connection.") @@ -96,10 +102,8 @@ def send_test(to: str, message: str) -> None: except httpx.TimeoutException: print("Error: Request timed out.") sys.exit(1) - except Exception as e: - # Mask token in error output to prevent credential leakage - safe_err = str(e).replace(token, _mask_secret(token)) if token else str(e) - print(f"Error: {safe_err}") + except Exception as exc: + print(f"Error: unexpected {exc.__class__.__name__} while sending the test message.") sys.exit(1) diff --git a/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/validate_config.py b/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/validate_config.py index ab6a90e1..ff893851 100644 --- a/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/validate_config.py +++ b/plugins/antigravity-awesome-skills-claude/skills/whatsapp-cloud-api/scripts/validate_config.py @@ -47,11 +47,14 @@ def check_env_vars() -> tuple[bool, list[str]]: return len(missing) == 0, missing -def _mask_secret(value: str) -> str: - """Return a masked version of a secret for safe logging.""" - if not value or len(value) < 8: - return "***masked***" - return f"{value[:6]}...masked" +def _format_api_failure(response: httpx.Response) -> str: + """Return a sanitized API failure message without echoing sensitive payloads.""" + try: + error = response.json().get("error", {}) + except ValueError: + error = {} + error_code = error.get("code", "?") + return f"API request failed (status {response.status_code}, code {error_code})." def test_api_connection() -> tuple[bool, str]: @@ -76,17 +79,14 @@ def test_api_connection() -> tuple[bool, str]: f" Quality: {data.get('quality_rating', 'N/A')}" ) else: - error = response.json().get("error", {}) - return False, f"API Error {error.get('code', '?')}: {error.get('message', 'Unknown')}" + return False, _format_api_failure(response) except httpx.ConnectError: return False, "Connection failed. Check your internet connection." except httpx.TimeoutException: return False, "Request timed out after 10 seconds." - except Exception as e: - # Mask token in error output to prevent credential leakage - safe_err = str(e).replace(token, _mask_secret(token)) if token else str(e) - return False, f"Unexpected error: {safe_err}" + except Exception as exc: + return False, f"Unexpected {exc.__class__.__name__} while contacting the Graph API." def test_waba_access() -> tuple[bool, str]: @@ -106,13 +106,10 @@ def test_waba_access() -> tuple[bool, str]: count = len(data.get("data", [])) return True, f"WABA accessible. {count} phone number(s) found." else: - error = response.json().get("error", {}) - return False, f"API Error {error.get('code', '?')}: {error.get('message', 'Unknown')}" + return False, _format_api_failure(response) - except Exception as e: - # Mask token in error output to prevent credential leakage - safe_err = str(e).replace(token, _mask_secret(token)) if token else str(e) - return False, f"Error: {safe_err}" + except Exception as exc: + return False, f"Unexpected {exc.__class__.__name__} while checking WABA access." def main(): diff --git a/plugins/antigravity-awesome-skills/skills/claude-monitor/scripts/api_bench.py b/plugins/antigravity-awesome-skills/skills/claude-monitor/scripts/api_bench.py index ce01cee4..f210af2e 100644 --- a/plugins/antigravity-awesome-skills/skills/claude-monitor/scripts/api_bench.py +++ b/plugins/antigravity-awesome-skills/skills/claude-monitor/scripts/api_bench.py @@ -34,6 +34,17 @@ ENDPOINTS = [ ] +def create_tls_context(): + """Cria contexto TLS restringindo conexoes a TLS 1.2+.""" + context = ssl.create_default_context() + if hasattr(ssl, "TLSVersion"): + context.minimum_version = ssl.TLSVersion.TLSv1_2 + else: + context.options |= getattr(ssl, "OP_NO_TLSv1", 0) + context.options |= getattr(ssl, "OP_NO_TLSv1_1", 0) + return context + + def test_tcp_latency(host, port, timeout=5): """Testa latĂȘncia TCP para um host:port.""" try: @@ -49,7 +60,7 @@ def test_tcp_latency(host, port, timeout=5): def test_tls_handshake(host, port=443, timeout=5): """Testa tempo do handshake TLS.""" try: - context = ssl.create_default_context() + context = create_tls_context() start = time.time() with socket.create_connection((host, port), timeout=timeout) as sock: with context.wrap_socket(sock, server_hostname=host) as ssock: diff --git a/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/send_test_message.py b/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/send_test_message.py index 7f121a1f..99196135 100644 --- a/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/send_test_message.py +++ b/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/send_test_message.py @@ -28,11 +28,21 @@ except ImportError: GRAPH_API = "https://graph.facebook.com/v21.0" -def _mask_secret(value: str) -> str: - """Return a masked version of a secret for safe logging.""" - if not value or len(value) < 8: - return "***masked***" - return f"{value[:6]}...masked" +def _redact_json(value): + """Recursively redact common secret-bearing keys before logging JSON.""" + sensitive_keys = {"authorization", "token", "access_token", "app_secret", "secret"} + + if isinstance(value, dict): + redacted = {} + for key, item in value.items(): + if key.lower() in sensitive_keys: + redacted[key] = "***redacted***" + else: + redacted[key] = _redact_json(item) + return redacted + if isinstance(value, list): + return [_redact_json(item) for item in value] + return value def send_test(to: str, message: str) -> None: @@ -84,11 +94,7 @@ def send_test(to: str, message: str) -> None: print() print("Full response:") - # Mask token in response output to prevent credential leakage - response_str = json.dumps(data, indent=2) - if token and token in response_str: - response_str = response_str.replace(token, _mask_secret(token)) - print(response_str) + print(json.dumps(_redact_json(data), indent=2)) except httpx.ConnectError: print("Error: Connection failed. Check your internet connection.") @@ -96,10 +102,8 @@ def send_test(to: str, message: str) -> None: except httpx.TimeoutException: print("Error: Request timed out.") sys.exit(1) - except Exception as e: - # Mask token in error output to prevent credential leakage - safe_err = str(e).replace(token, _mask_secret(token)) if token else str(e) - print(f"Error: {safe_err}") + except Exception as exc: + print(f"Error: unexpected {exc.__class__.__name__} while sending the test message.") sys.exit(1) diff --git a/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/validate_config.py b/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/validate_config.py index ab6a90e1..ff893851 100644 --- a/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/validate_config.py +++ b/plugins/antigravity-awesome-skills/skills/whatsapp-cloud-api/scripts/validate_config.py @@ -47,11 +47,14 @@ def check_env_vars() -> tuple[bool, list[str]]: return len(missing) == 0, missing -def _mask_secret(value: str) -> str: - """Return a masked version of a secret for safe logging.""" - if not value or len(value) < 8: - return "***masked***" - return f"{value[:6]}...masked" +def _format_api_failure(response: httpx.Response) -> str: + """Return a sanitized API failure message without echoing sensitive payloads.""" + try: + error = response.json().get("error", {}) + except ValueError: + error = {} + error_code = error.get("code", "?") + return f"API request failed (status {response.status_code}, code {error_code})." def test_api_connection() -> tuple[bool, str]: @@ -76,17 +79,14 @@ def test_api_connection() -> tuple[bool, str]: f" Quality: {data.get('quality_rating', 'N/A')}" ) else: - error = response.json().get("error", {}) - return False, f"API Error {error.get('code', '?')}: {error.get('message', 'Unknown')}" + return False, _format_api_failure(response) except httpx.ConnectError: return False, "Connection failed. Check your internet connection." except httpx.TimeoutException: return False, "Request timed out after 10 seconds." - except Exception as e: - # Mask token in error output to prevent credential leakage - safe_err = str(e).replace(token, _mask_secret(token)) if token else str(e) - return False, f"Unexpected error: {safe_err}" + except Exception as exc: + return False, f"Unexpected {exc.__class__.__name__} while contacting the Graph API." def test_waba_access() -> tuple[bool, str]: @@ -106,13 +106,10 @@ def test_waba_access() -> tuple[bool, str]: count = len(data.get("data", [])) return True, f"WABA accessible. {count} phone number(s) found." else: - error = response.json().get("error", {}) - return False, f"API Error {error.get('code', '?')}: {error.get('message', 'Unknown')}" + return False, _format_api_failure(response) - except Exception as e: - # Mask token in error output to prevent credential leakage - safe_err = str(e).replace(token, _mask_secret(token)) if token else str(e) - return False, f"Error: {safe_err}" + except Exception as exc: + return False, f"Unexpected {exc.__class__.__name__} while checking WABA access." def main(): diff --git a/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json b/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json index b4d6c0e2..64387bb9 100644 --- a/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +++ b/skills/loki-mode/examples/todo-app-generated/backend/package-lock.json @@ -1110,9 +1110,9 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, "node_modules/prebuild-install": { diff --git a/skills/loki-mode/examples/todo-app-generated/backend/package.json b/skills/loki-mode/examples/todo-app-generated/backend/package.json index 4cec7d90..805ca3f8 100644 --- a/skills/loki-mode/examples/todo-app-generated/backend/package.json +++ b/skills/loki-mode/examples/todo-app-generated/backend/package.json @@ -24,6 +24,7 @@ }, "overrides": { "diff": "4.0.4", + "path-to-regexp": "0.1.13", "qs": "^6.15.0" } } diff --git a/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json b/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json index 2a4b1d15..4d91dee8 100644 --- a/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +++ b/skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json @@ -1527,9 +1527,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { diff --git a/skills/loki-mode/examples/todo-app-generated/frontend/package.json b/skills/loki-mode/examples/todo-app-generated/frontend/package.json index 9b0fcdae..b860b5c2 100644 --- a/skills/loki-mode/examples/todo-app-generated/frontend/package.json +++ b/skills/loki-mode/examples/todo-app-generated/frontend/package.json @@ -23,6 +23,7 @@ "react-dom": "^19.2.3" }, "overrides": { + "picomatch": "4.0.4", "rollup": "^4.59.0" } }