mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
feat(ne-exec): make max CLI commands configurable and allow traceroute
Add NETX_NE_EXEC_MAX_COMMANDS (default 5, cap 50) and whitelist traceroute/tracert/trace/trace6 prefixes for lab/ops use. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
a6a5abc9ef
commit
88479245bb
9 changed files with 116 additions and 15 deletions
|
|
@ -72,6 +72,8 @@ class Settings(BaseSettings):
|
|||
ne_collect_pending_stale_sec: int = 180
|
||||
ne_collect_run_timeout_cap_sec: int = 600
|
||||
ne_collection_data_dir: str = "data/ne_collections"
|
||||
# Managed NE exec: max CLI commands per request (lab can raise; hard-capped in ne_exec).
|
||||
ne_exec_max_commands: int = 5
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ def api_delete_ume_synced_managed_ne(db: Session = Depends(get_db)):
|
|||
|
||||
@router.post("/exec")
|
||||
def api_exec_managed_ne(body: ManagedNeExecRequest, db: Session = Depends(get_db)):
|
||||
"""Login to a managed NE or UME inventory NE and run read-only CLI (show/display/ping)."""
|
||||
"""Login to a managed NE or UME inventory NE and run read-only CLI (show/display/ping/traceroute)."""
|
||||
return execute_managed_ne_commands(
|
||||
db,
|
||||
body.commands,
|
||||
|
|
|
|||
|
|
@ -13,11 +13,16 @@ from .config import settings
|
|||
from .ne_collect_runner import _collect_on_device
|
||||
from .ne_crypto import credentials_configured
|
||||
|
||||
_EXEC_MAX_COMMANDS = 5
|
||||
_EXEC_MAX_COMMANDS_CAP = 50
|
||||
_EXEC_MAX_OUTPUT = 32_000
|
||||
_EXEC_READ_TIMEOUT_DEFAULT = 60
|
||||
_EXEC_READ_TIMEOUT_MAX = 120
|
||||
|
||||
|
||||
def _exec_max_commands() -> int:
|
||||
raw = int(settings.ne_exec_max_commands or 5)
|
||||
return max(1, min(_EXEC_MAX_COMMANDS_CAP, raw))
|
||||
|
||||
# Block obvious config-change / destructive patterns (case-insensitive).
|
||||
_BLOCKED_RE = re.compile(
|
||||
r"(?i)("
|
||||
|
|
@ -29,8 +34,10 @@ _BLOCKED_RE = re.compile(
|
|||
r")"
|
||||
)
|
||||
|
||||
# Read-only CLI: show/display plus ping reachability checks.
|
||||
_ALLOWED_PREFIX_RE = re.compile(r"(?i)^(show\s|display\s|ping\s|ping6\s)")
|
||||
# Read-only CLI: show/display plus ping/traceroute reachability checks.
|
||||
_ALLOWED_PREFIX_RE = re.compile(
|
||||
r"(?i)^(show\s|display\s|ping\s|ping6\s|traceroute\s|tracert\s|trace\s|trace6\s)"
|
||||
)
|
||||
|
||||
# Unicode / C1 line separators that can smuggle a second CLI after a show prefix.
|
||||
_FORBIDDEN_LINE_SEPARATORS = ("\u2028", "\u2029", "\x85", "\x0b", "\x0c")
|
||||
|
|
@ -94,8 +101,9 @@ def execute_managed_ne_commands(
|
|||
cmds = [str(c).strip() for c in commands if str(c).strip()]
|
||||
if not cmds:
|
||||
raise HTTPException(status_code=400, detail="commands_required")
|
||||
if len(cmds) > _EXEC_MAX_COMMANDS:
|
||||
raise HTTPException(status_code=400, detail=f"too_many_commands (max {_EXEC_MAX_COMMANDS})")
|
||||
max_cmds = _exec_max_commands()
|
||||
if len(cmds) > max_cmds:
|
||||
raise HTTPException(status_code=400, detail=f"too_many_commands (max {max_cmds})")
|
||||
for c in cmds:
|
||||
_validate_command(c)
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,8 @@ class ManagedNeExecRequest(BaseModel):
|
|||
|
||||
ne_id: str | None = None
|
||||
ume_ne_id: str | None = None
|
||||
commands: list[str] = Field(min_length=1, max_length=5)
|
||||
# Absolute ceiling; runtime limit is settings.ne_exec_max_commands (default 5).
|
||||
commands: list[str] = Field(min_length=1, max_length=50)
|
||||
read_timeout_sec: int | None = Field(default=None, ge=10, le=120)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue