Coderabbit fixes:

- Mac
- Flask debug
- Threaded flask
- propagate token in GET requests
- enhance spec docs
- normalize MAC x2
- mcp disablement redundant private attribute
- run all tests imports
This commit is contained in:
Adam Outler
2026-01-19 00:03:27 +00:00
parent ecea1d1fbd
commit bb0c0e1c74
13 changed files with 326 additions and 55 deletions

View File

@@ -13,7 +13,7 @@ INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from logger import mylog # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value, get_env_setting_value # noqa: E402 [flake8 lint suppression]
from db.db_helper import get_date_from_period # noqa: E402 [flake8 lint suppression]
from app_state import updateState # noqa: E402 [flake8 lint suppression]
@@ -1693,10 +1693,26 @@ def start_server(graphql_port, app_state):
if app_state.graphQLServerStarted == 0:
mylog("verbose", [f"[graphql endpoint] Starting on port: {graphql_port}"])
# First check environment variable override (direct env like FLASK_DEBUG)
env_val = get_env_setting_value("FLASK_DEBUG", None)
if env_val is not None:
flask_debug = bool(env_val)
mylog("verbose", [f"[graphql endpoint] Flask debug mode: {flask_debug} (FLASK_DEBUG env override)"])
else:
# Fall back to configured setting `FLASK_DEBUG` (from app.conf / overrides)
flask_debug = get_setting_value("FLASK_DEBUG")
# Normalize value to boolean in case it's stored as a string
if isinstance(flask_debug, str):
flask_debug = flask_debug.strip().lower() in ("1", "true", "yes", "on")
else:
flask_debug = bool(flask_debug)
mylog("verbose", [f"[graphql endpoint] Flask debug mode: {flask_debug} (FLASK_DEBUG setting)"])
# Start Flask app in a separate thread
thread = threading.Thread(
target=lambda: app.run(
host="0.0.0.0", port=graphql_port, debug=False, use_reloader=False
host="0.0.0.0", port=graphql_port, threaded=True,debug=flask_debug, use_reloader=False
)
)
thread.start()

View File

@@ -642,6 +642,11 @@ def _execute_tool(route: Dict[str, Any], args: Dict[str, Any]) -> Dict[str, Any]
headers = {"Content-Type": "application/json"}
if "Authorization" in request.headers:
headers["Authorization"] = request.headers["Authorization"]
else:
# Propagate query token or fallback to configured API token for internal loopback
token = request.args.get("token") or get_setting_value('API_TOKEN')
if token:
headers["Authorization"] = f"Bearer {token}"
filtered_body_args = {k: v for k, v in args.items() if f"{{{k}}}" not in route['path']}

View File

@@ -4,7 +4,7 @@ from typing import Dict, Any, Optional, Type, List
from pydantic import BaseModel
def pydantic_to_json_schema(model: Type[BaseModel]) -> Dict[str, Any]:
def pydantic_to_json_schema(model: Type[BaseModel], mode: str = "validation") -> Dict[str, Any]:
"""
Convert a Pydantic model to JSON Schema (OpenAPI 3.1 compatible).
@@ -13,12 +13,13 @@ def pydantic_to_json_schema(model: Type[BaseModel]) -> Dict[str, Any]:
Args:
model: Pydantic BaseModel class
mode: Schema mode - "validation" (for inputs) or "serialization" (for outputs)
Returns:
JSON Schema dictionary
"""
# Pydantic v2 uses model_json_schema()
schema = model.model_json_schema(mode="serialization")
schema = model.model_json_schema(mode=mode)
# Remove $defs if empty (cleaner output)
if "$defs" in schema and not schema["$defs"]:
@@ -169,7 +170,7 @@ def build_responses(
# Success response (200)
if response_model:
# Strip validation from response schema to save tokens
schema = strip_validation(pydantic_to_json_schema(response_model))
schema = strip_validation(pydantic_to_json_schema(response_model, mode="serialization"))
schema = extract_definitions(schema, definitions)
responses["200"] = {
"description": "Successful response",

View File

@@ -361,6 +361,42 @@ def setting_value_to_python_type(set_type, set_value):
return value
# -------------------------------------------------------------------------------
# Environment helper
def get_env_setting_value(key, default=None):
"""Return a typed value from environment variable if present.
- Parses booleans (1/0, true/false, yes/no, on/off).
- Tries to parse ints and JSON literals where sensible.
- Returns `default` when env var is not set.
"""
val = os.environ.get(key)
if val is None:
return default
v = val.strip()
# Booleans
low = v.lower()
if low in ("1", "true", "yes", "on"):
return True
if low in ("0", "false", "no", "off"):
return False
# Integer
try:
if re.fullmatch(r"-?\d+", v):
return int(v)
except Exception:
pass
# JSON-like (list/object/true/false/null/number)
try:
return json.loads(v)
except Exception:
# Fallback to raw string
return v
# -------------------------------------------------------------------------------
def updateSubnets(scan_subnets):
"""

View File

@@ -334,15 +334,6 @@ def importConfigs(pm, db, all_plugins):
"[]",
"General",
)
conf.FLASK_DEBUG = ccd(
"FLASK_DEBUG",
False,
c_d,
"Flask debug mode - SECURITY WARNING: Enabling enables interactive debugger with RCE risk. Configure via environment only, not exposed in UI.",
'{"dataType": "boolean","elements": []}',
"[]",
"system",
)
conf.VERSION = ccd(
"VERSION",
"",

View File

@@ -4,7 +4,7 @@ import re
import sqlite3
import csv
from io import StringIO
from front.plugins.plugin_helper import is_mac
from front.plugins.plugin_helper import is_mac, normalize_mac
from logger import mylog
from models.plugin_object_instance import PluginObjectInstance
from database import get_temp_db_connection
@@ -500,6 +500,9 @@ class DeviceInstance:
def setDeviceData(self, mac, data):
"""Update or create a device."""
normalized_mac = normalize_mac(mac)
normalized_parent_mac = normalize_mac(data.get("devParentMAC") or "")
conn = None
try:
if data.get("createNew", False):
@@ -517,7 +520,7 @@ class DeviceInstance:
"""
values = (
mac,
normalized_mac,
data.get("devName") or "",
data.get("devOwner") or "",
data.get("devType") or "",
@@ -527,7 +530,7 @@ class DeviceInstance:
data.get("devGroup") or "",
data.get("devLocation") or "",
data.get("devComments") or "",
data.get("devParentMAC") or "",
normalized_parent_mac,
data.get("devParentPort") or "",
data.get("devSSID") or "",
data.get("devSite") or "",
@@ -569,7 +572,7 @@ class DeviceInstance:
data.get("devGroup") or "",
data.get("devLocation") or "",
data.get("devComments") or "",
data.get("devParentMAC") or "",
normalized_parent_mac,
data.get("devParentPort") or "",
data.get("devSSID") or "",
data.get("devSite") or "",
@@ -583,7 +586,7 @@ class DeviceInstance:
data.get("devIsNew") or 0,
data.get("devIsArchived") or 0,
data.get("devCustomProps") or "",
mac,
normalized_mac,
)
conn = get_temp_db_connection()