mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 04:20:45 +08:00
Skip WebCRT audit for empty Enter on prompt-only lines.
Do not reuse the previous command when the operator presses Enter on a blank prompt; validate prompt-only rows on both frontend and backend. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b35e56afb0
commit
7ca57278fe
4 changed files with 109 additions and 21 deletions
|
|
@ -539,6 +539,34 @@ def _is_prompt_command_line(line: str) -> bool:
|
|||
)
|
||||
|
||||
|
||||
def is_auditable_command_line(line: str) -> bool:
|
||||
"""False for empty Enter (prompt only, no command text)."""
|
||||
s = normalize_audit_line(line)
|
||||
if not s.strip():
|
||||
return False
|
||||
if _is_prompt_only_line(s):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _is_prompt_only_line(line: str) -> bool:
|
||||
"""True when line is a device prompt with no command typed."""
|
||||
s = normalize_audit_line(line)
|
||||
if not s:
|
||||
return True
|
||||
return bool(
|
||||
re.match(
|
||||
r"^(?:"
|
||||
r"[\w.-]+(?:\([^)]+\))*[#>]\s*"
|
||||
r"|<[^>]+>\s*"
|
||||
r"|\[[^\]]+\]\s*"
|
||||
r")$",
|
||||
s,
|
||||
flags=re.I,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def extract_last_prompt_command(text: str) -> str | None:
|
||||
"""Last prompt+command line in PTY transcript (tab-complete redraw aware).
|
||||
|
||||
|
|
|
|||
|
|
@ -370,16 +370,28 @@ class WebcrtSession:
|
|||
if redacted and (buf_lines or audit_line):
|
||||
self._password_mode = False
|
||||
if "\r" in text or "\n" in text:
|
||||
from .webcrt_channel import extract_last_prompt_command, normalize_audit_line
|
||||
from .webcrt_channel import (
|
||||
extract_last_prompt_command,
|
||||
is_auditable_command_line,
|
||||
normalize_audit_line,
|
||||
)
|
||||
|
||||
# Ground truth: xterm visible row at Enter (frontend). PTY stdout/stdin
|
||||
# still carry intermediate backspaces / tab redraws — do not prefer those.
|
||||
cmd: str | None = None
|
||||
if audit_line and str(audit_line).strip():
|
||||
cmd = normalize_audit_line(audit_line)
|
||||
else:
|
||||
raw = extract_last_prompt_command(self._stdout_tail) or self._last_prompt_line
|
||||
cmd = raw if raw else (buf_lines[-1] if buf_lines else None)
|
||||
lines = [cmd] if cmd and str(cmd).strip() else []
|
||||
candidate = normalize_audit_line(audit_line)
|
||||
if is_auditable_command_line(candidate):
|
||||
cmd = candidate
|
||||
if not cmd:
|
||||
raw = extract_last_prompt_command(self._stdout_tail)
|
||||
if raw and is_auditable_command_line(raw):
|
||||
cmd = raw
|
||||
elif buf_lines and str(buf_lines[-1]).strip():
|
||||
last = str(buf_lines[-1]).strip()
|
||||
if is_auditable_command_line(last):
|
||||
cmd = last
|
||||
lines = [cmd] if cmd else []
|
||||
self._last_prompt_line = ""
|
||||
else:
|
||||
lines = []
|
||||
|
|
@ -415,6 +427,12 @@ class WebcrtSession:
|
|||
line = extract_last_prompt_command(self._stdout_tail)
|
||||
if line:
|
||||
self._last_prompt_line = line
|
||||
else:
|
||||
from .webcrt_channel import _is_prompt_only_line, _strip_ansi
|
||||
|
||||
tail = _strip_ansi(self._stdout_tail).rsplit("\r", 1)[-1].split("\n")[-1]
|
||||
if _is_prompt_only_line(tail):
|
||||
self._last_prompt_line = ""
|
||||
if looks_like_password_prompt(self._stdout_tail):
|
||||
self._password_mode = True
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue