144 lines
6.3 KiB
Markdown
144 lines
6.3 KiB
Markdown
# Phase 1.0Y: offline shsrv framing audit
|
|
|
|
Status: `SOURCE_FAMILY_FRAMING_PARTIAL_PROMPT_COMPLETION_UNPROVEN`
|
|
|
|
Date: 2026-07-22
|
|
|
|
This phase audits only clean, already-local official shsrv Git history and
|
|
implements a pure host model. It creates no socket, address, live CLI, command
|
|
formatter or target artifact. No device action occurred.
|
|
|
|
## Bound source history
|
|
|
|
| Reference | Commit | Framing family |
|
|
|---|---|---|
|
|
| v0.7 | `74287f5db6b20320efd7892d7b29cf438fe7cb98` | legacy raw |
|
|
| v0.8 | `8f76139ee69df4b8cb7c3aee401f05bccb2c2a31` | legacy raw |
|
|
| v0.9 | `2f2bc5501d40064c18c06f06f7b1f4cab756389b` | libtelnet/NVT introduced |
|
|
| v0.19/current | `6f320637d56d344a0e7797753099e33238bbf146` | libtelnet/NVT |
|
|
|
|
Commit `2f2bc55` added `libtelnet.c`, `libtelnet.h` and the Telnet state machine
|
|
to `sh.c`. The current reference is also official tag v0.19. Both local trees
|
|
are clean. Every local official tag from v0.9 through v0.19 was checked for the
|
|
same empty option table, NVT flag, send path and `libtelnet.c` presence. These
|
|
facts identify official source families; they do not identify the deployed
|
|
binary.
|
|
|
|
## Byte-flow graph
|
|
|
|
```text
|
|
legacy v0.7-v0.8:
|
|
peer bytes -> socket-backed stdin -> sh_readline
|
|
stdout/stderr -> socket bytes unchanged
|
|
|
|
v0.9-v0.19:
|
|
peer bytes -> read(4096) -> telnet_recv/NVT -> pipe -> sh_readline
|
|
stdout/stderr -> pipe -> read(4096) -> telnet_send_text/NVT -> peer
|
|
```
|
|
|
|
The current server configures an empty Telnet option table and does not call
|
|
`telnet_negotiate`. It therefore sends no proactive negotiation bytes. From
|
|
the initial RFC1143 state, received `IAC WILL x` produces `IAC DONT x`, and
|
|
received `IAC DO x` produces `IAC WONT x`; initial `WONT` and `DONT` produce no
|
|
reply. A client that sends only plain command bytes triggers none of these
|
|
replies.
|
|
|
|
## Exact source-family transformations
|
|
|
|
| Property | legacy raw | libtelnet/NVT |
|
|
|---|---|---|
|
|
| incoming plain bytes | unchanged | unchanged except NVT EOL |
|
|
| incoming CR LF | remains CR LF; CR is later an argument delimiter | becomes LF |
|
|
| incoming CR NUL | unchanged | becomes CR |
|
|
| incoming IAC IAC | remains two bytes | becomes one IAC application byte |
|
|
| outgoing LF | LF | CR LF |
|
|
| outgoing CR | CR | CR NUL |
|
|
| outgoing IAC | one IAC | doubled IAC |
|
|
| proactive negotiation | none | none |
|
|
| server-side command echo | none in source | none in source |
|
|
|
|
The official host wrapper invokes an external `telnet` program, optionally
|
|
under `rlwrap`. Local echo, negotiation defaults, chunking and display behavior
|
|
therefore belong to that external client and are not defined by shsrv. The
|
|
wrapper has no bounded evidence, sanitization or timeout contract and is not a
|
|
Chimera client candidate.
|
|
|
|
## Prompt and completion
|
|
|
|
Both source families use the same control flow:
|
|
|
|
1. `sh_greet` writes the automatic greeting;
|
|
2. `sh_prompt` writes `PWD + "$ "` without a newline and flushes;
|
|
3. `sh_readline` waits for LF;
|
|
4. `help` completes synchronously, while forked `stat`/`sum` are waited for;
|
|
5. all streams are flushed and the loop emits the next prompt.
|
|
|
|
This proves source ordering but not an exact live boundary:
|
|
|
|
- `setenv("PWD", "/", 0)` does not overwrite an inherited `PWD`;
|
|
- the prompt has no length or prefix field;
|
|
- arbitrary inherited `PWD` can itself contain the two-byte `$ ` shape;
|
|
- an external Telnet client's local echo is outside the server contract;
|
|
- stdout, stderr and negotiation writes can be chunked/interleaved;
|
|
- current `sh_on_telnet_data` and `sh_on_telnet_send` each use one `write` and
|
|
only log a short write; they do not finish the remaining bytes;
|
|
- the exact deployed source family and binary remain unproven.
|
|
|
|
The allowlisted Phase-1.0W path alphabet excludes whitespace, `$`, pipe and
|
|
shell separators. The audited v0.19 static strings for `help`, `stat` and `sum`
|
|
contain no `$ ` except `sh_prompt`. This makes a terminal `$ ` a useful
|
|
source-shaped candidate under that exact source, but not a deployed completion
|
|
attestation. The offline model therefore never returns
|
|
`exact_completion_proven=true`.
|
|
|
|
## Command framing evidence
|
|
|
|
`sh_readline` consumes one byte at a time and ends a command at LF. Both source
|
|
families therefore accept plain LF termination. In the legacy family, an
|
|
external Telnet client's preceding CR is removed later by the argument-token
|
|
delimiter. Multiple already-buffered newline-terminated commands would be
|
|
processed sequentially by the source loop.
|
|
|
|
No Chimera command-byte formatter is added here. A future passive client
|
|
contract can avoid Telnet negotiation entirely and use a bounded, prevalidated
|
|
LF-only batch. That claim remains design-only until separately implemented and
|
|
tested.
|
|
|
|
## Robustness and side effects
|
|
|
|
| Finding | Classification |
|
|
|---|---|
|
|
| automatic model/serial/firmware/temperature/frequency greeting | `HIGH_FUNCTIONAL_ACCEPTANCE_REQUIRED` |
|
|
| current single-write handling for pipe and socket | `PROTOCOL_INTEGRITY_RISK` |
|
|
| no server-side session deadline | `SESSION_HANG_RISK` |
|
|
| IAC IP/SUSP/ABORT may signal a running child | `HIGH_FUNCTIONAL_EXCLUDED_INPUT` |
|
|
| legacy Telnet controls pass into the shell byte stream | `PROTOCOL_INTEGRITY_RISK` |
|
|
| disconnect exits the shell process; outer server can accept again | `SOURCE_DESIGN_ONLY_RUNTIME_UNPROVEN` |
|
|
| no live filesystem write or autoload in framing model | `SAFE_OFFLINE` |
|
|
|
|
IAC command generation is excluded from every future Chimera plan. The model
|
|
records synthetic IAC commands so tests cannot silently treat them as text.
|
|
|
|
## Offline model
|
|
|
|
`tools/phase10y_shsrv_framing_model.py` models both source families with fixed
|
|
65,536-byte and 256-chunk limits. It covers NVT EOL, IAC escaping, initial
|
|
unsupported-option replies, subnegotiation removal and prompt-candidate
|
|
classification. It has no network import, CLI, target, command sender or file
|
|
output. Its 22 tests use synthetic bytes only.
|
|
|
|
## Decision
|
|
|
|
- official framing families: `PROVEN_FROM_LOCAL_OFFICIAL_SOURCE`;
|
|
- deployed family and binary: `UNPROVEN`;
|
|
- source prompt order: `PROVEN_FROM_SOURCE`;
|
|
- terminal `$ ` recognition: `SOURCE_SHAPE_CANDIDATE_ONLY`;
|
|
- exact live command completion: `UNPROVEN`;
|
|
- passive no-negotiation approach: `OFFLINE_DESIGN_CANDIDATE`;
|
|
- live network adapter or collection: `NOT_AUTHORIZED`.
|
|
|
|
The next permitted step is an offline-only passive, source-family-tolerant
|
|
batch contract. It must retain deadline sealing and partial-result rejection,
|
|
must never emit Telnet commands, and still may not add a real socket or device
|
|
address.
|