@@ -2234,3 +2234,115 @@ func TestServe_CSRF_TokenRequired(t *testing.T) {
22342234 conn = dialTestWS (t , addr )
22352235 conn .Close ()
22362236}
2237+
2238+ // TestServe_E2E_PromptSizeCap verifies that prompts above maxPromptBytes are
2239+ // rejected server-side before they are stored in the session or forwarded to
2240+ // the LLM (finding #69).
2241+ func TestServe_E2E_PromptSizeCap (t * testing.T ) {
2242+ llmSrv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
2243+ if r .Method == http .MethodGet && strings .HasSuffix (r .URL .Path , "/models" ) {
2244+ w .Header ().Set ("Content-Type" , "application/json" )
2245+ json .NewEncoder (w ).Encode (map [string ]any {"data" : []map [string ]any {}})
2246+ return
2247+ }
2248+ t .Error ("LLM must not be called for an oversized prompt" )
2249+ w .WriteHeader (http .StatusOK )
2250+ }))
2251+ defer llmSrv .Close ()
2252+
2253+ envCleanup := setTestEnv (t , llmSrv .URL )
2254+ defer envCleanup ()
2255+
2256+ store := newTestSessionStore (t )
2257+ ln , mux := buildServeMux (t , store )
2258+ defer ln .Close ()
2259+
2260+ errCh := make (chan error , 1 )
2261+ go func () { errCh <- serveOnListener (ln , mux ) }()
2262+ waitForHTTP (t , ln .Addr ().String ())
2263+
2264+ conn := dialTestWS (t , ln .Addr ().String ())
2265+ defer conn .Close ()
2266+ conn .SetReadDeadline (time .Now ().Add (5 * time .Second ))
2267+
2268+ msg := map [string ]any {
2269+ "type" : "prompt" ,
2270+ "content" : strings .Repeat ("x" , maxPromptBytes + 1 ),
2271+ }
2272+ payload , _ := json .Marshal (msg )
2273+ if err := golangws .Message .Send (conn , string (payload )); err != nil {
2274+ t .Fatalf ("Send: %v" , err )
2275+ }
2276+
2277+ var raw []byte
2278+ if err := golangws .Message .Receive (conn , & raw ); err != nil {
2279+ t .Fatalf ("expected error event, got receive error: %v" , err )
2280+ }
2281+ var evt map [string ]any
2282+ if err := json .Unmarshal (raw , & evt ); err != nil {
2283+ t .Fatalf ("unmarshal event: %v" , err )
2284+ }
2285+ if evt ["type" ] != "error" {
2286+ t .Fatalf ("expected error event for oversized prompt, got %v" , evt ["type" ])
2287+ }
2288+ }
2289+
2290+ // TestServe_E2E_InvalidModelIDRejected verifies that model IDs from the Web UI
2291+ // are length- and character-validated before use (finding #81).
2292+ func TestServe_E2E_InvalidModelIDRejected (t * testing.T ) {
2293+ llmSrv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
2294+ if r .Method == http .MethodGet && strings .HasSuffix (r .URL .Path , "/models" ) {
2295+ w .Header ().Set ("Content-Type" , "application/json" )
2296+ json .NewEncoder (w ).Encode (map [string ]any {"data" : []map [string ]any {}})
2297+ return
2298+ }
2299+ t .Error ("LLM must not be called for an invalid model ID" )
2300+ w .WriteHeader (http .StatusOK )
2301+ }))
2302+ defer llmSrv .Close ()
2303+
2304+ envCleanup := setTestEnv (t , llmSrv .URL )
2305+ defer envCleanup ()
2306+
2307+ store := newTestSessionStore (t )
2308+ ln , mux := buildServeMux (t , store )
2309+ defer ln .Close ()
2310+
2311+ errCh := make (chan error , 1 )
2312+ go func () { errCh <- serveOnListener (ln , mux ) }()
2313+ waitForHTTP (t , ln .Addr ().String ())
2314+
2315+ conn := dialTestWS (t , ln .Addr ().String ())
2316+ defer conn .Close ()
2317+ conn .SetReadDeadline (time .Now ().Add (5 * time .Second ))
2318+
2319+ cases := []string {
2320+ strings .Repeat ("x" , maxModelIDBytes + 1 ),
2321+ "model\n with-newline" ,
2322+ "model\x00 with-null" ,
2323+ "model<script>" ,
2324+ }
2325+ for _ , model := range cases {
2326+ msg := map [string ]any {
2327+ "type" : "prompt" ,
2328+ "content" : "hello" ,
2329+ "model" : model ,
2330+ }
2331+ payload , _ := json .Marshal (msg )
2332+ if err := golangws .Message .Send (conn , string (payload )); err != nil {
2333+ t .Fatalf ("Send: %v" , err )
2334+ }
2335+
2336+ var raw []byte
2337+ if err := golangws .Message .Receive (conn , & raw ); err != nil {
2338+ t .Fatalf ("expected error event, got receive error: %v" , err )
2339+ }
2340+ var evt map [string ]any
2341+ if err := json .Unmarshal (raw , & evt ); err != nil {
2342+ t .Fatalf ("unmarshal event: %v" , err )
2343+ }
2344+ if evt ["type" ] != "error" {
2345+ t .Fatalf ("expected error event for model %q, got %v" , model , evt ["type" ])
2346+ }
2347+ }
2348+ }
0 commit comments