@@ -23,7 +23,7 @@ function makeHandler() {
2323 url : "http://127.0.0.1:4321/s/s1" ,
2424 project : "repo" ,
2525 label : "plan-repo" ,
26- htmlContent : "<html><head></head><body>Plan</body></html>" ,
26+ htmlContent : "<html><script>const literal='</head>';</script>< head></head><body>Plan</body></html>" ,
2727 handleRequest : ( _req , url ) => Response . json ( { path : url . pathname } ) ,
2828 } ) ,
2929 } ) ;
@@ -44,6 +44,7 @@ describe("daemon HTTP router", () => {
4444 const { handler } = makeHandler ( ) ;
4545 await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
4646 method : "POST" ,
47+ headers : { "content-type" : "application/json" } ,
4748 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
4849 } ) ) ;
4950 const res = await handler ( new Request ( "http://127.0.0.1:4321/daemon/status" ) ) ;
@@ -58,6 +59,7 @@ describe("daemon HTTP router", () => {
5859 const { handler } = makeHandler ( ) ;
5960 const create = await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
6061 method : "POST" ,
62+ headers : { "content-type" : "application/json" } ,
6163 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
6264 } ) ) ;
6365 expect ( create . status ) . toBe ( 201 ) ;
@@ -74,6 +76,7 @@ describe("daemon HTTP router", () => {
7476 const { handler } = makeHandler ( ) ;
7577 await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
7678 method : "POST" ,
79+ headers : { "content-type" : "application/json" } ,
7780 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
7881 } ) ) ;
7982 const res = await handler ( new Request ( "http://127.0.0.1:4321/s/s1" ) ) ;
@@ -82,12 +85,15 @@ describe("daemon HTTP router", () => {
8285 expect ( html ) . toContain ( "apiBase=\"/s/s1/api\"" ) ;
8386 expect ( html ) . toContain ( "window.fetch" ) ;
8487 expect ( html ) . toContain ( "window.EventSource" ) ;
88+ expect ( html . indexOf ( "window.__PLANNOTATOR_API_BASE__" ) ) . toBeGreaterThan ( html . indexOf ( "const literal" ) ) ;
89+ expect ( html . indexOf ( "window.__PLANNOTATOR_API_BASE__" ) ) . toBeLessThan ( html . indexOf ( "<body>" ) ) ;
8590 } ) ;
8691
8792 test ( "routes session-scoped API paths to the owning session" , async ( ) => {
8893 const { handler } = makeHandler ( ) ;
8994 await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
9095 method : "POST" ,
96+ headers : { "content-type" : "application/json" } ,
9197 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
9298 } ) ) ;
9399 const res = await handler ( new Request ( "http://127.0.0.1:4321/s/s1/api/plan" ) ) ;
@@ -100,6 +106,7 @@ describe("daemon HTTP router", () => {
100106 let timeoutDisabled = 0 ;
101107 await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
102108 method : "POST" ,
109+ headers : { "content-type" : "application/json" } ,
103110 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
104111 } ) ) ;
105112 const record = store . get ( "s1" ) ;
@@ -118,23 +125,36 @@ describe("daemon HTTP router", () => {
118125 expect ( timeoutDisabled ) . toBe ( 1 ) ;
119126 } ) ;
120127
121- test ( "routes legacy root API paths by session referer during UI migration " , async ( ) => {
128+ test ( "does not route root API paths by spoofable referer" , async ( ) => {
122129 const { handler } = makeHandler ( ) ;
123130 await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
124131 method : "POST" ,
132+ headers : { "content-type" : "application/json" } ,
125133 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
126134 } ) ) ;
127135 const res = await handler ( new Request ( "http://127.0.0.1:4321/api/plan" , {
128136 headers : { referer : "http://127.0.0.1:4321/s/s1" } ,
129137 } ) ) ;
138+ expect ( res . status ) . toBe ( 404 ) ;
139+ } ) ;
140+
141+ test ( "rejects non-JSON session creation requests" , async ( ) => {
142+ const { handler } = makeHandler ( ) ;
143+ const res = await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
144+ method : "POST" ,
145+ headers : { "content-type" : "text/plain" } ,
146+ body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
147+ } ) ) ;
130148 const body = await res . json ( ) ;
131- expect ( body . path ) . toBe ( "/api/plan" ) ;
149+ expect ( res . status ) . toBe ( 415 ) ;
150+ expect ( body . error . code ) . toBe ( "invalid-request" ) ;
132151 } ) ;
133152
134153 test ( "cancels sessions and returns result status" , async ( ) => {
135154 const { handler, store } = makeHandler ( ) ;
136155 await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions" , {
137156 method : "POST" ,
157+ headers : { "content-type" : "application/json" } ,
138158 body : JSON . stringify ( { request : { action : "plan" , origin : "opencode" , plan : "x" } } ) ,
139159 } ) ) ;
140160 const cancel = await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions/s1/cancel" , {
@@ -143,7 +163,9 @@ describe("daemon HTTP router", () => {
143163 expect ( ( await cancel . json ( ) ) . session . status ) . toBe ( "cancelled" ) ;
144164
145165 const result = await handler ( new Request ( "http://127.0.0.1:4321/daemon/sessions/s1/result" ) ) ;
146- expect ( ( await result . json ( ) ) . session . status ) . toBe ( "cancelled" ) ;
147- expect ( store . get ( "s1" ) ) . toBeUndefined ( ) ;
166+ const body = await result . json ( ) ;
167+ expect ( body . session . status ) . toBe ( "cancelled" ) ;
168+ expect ( body . session . error ) . toBe ( "Session cancelled." ) ;
169+ expect ( store . get ( "s1" ) ) . toBeDefined ( ) ;
148170 } ) ;
149171} ) ;
0 commit comments