@@ -373,3 +373,206 @@ describe('Agent endpoint security', () => {
373373 expect ( res . status ) . not . toBe ( 403 ) ;
374374 } ) ;
375375} ) ;
376+
377+ // ---------------------------------------------------------------
378+ // No-user mode — remote access guard
379+ // ---------------------------------------------------------------
380+
381+ describe ( 'Agent endpoint no-user mode' , ( ) => {
382+ let server ;
383+ let port ;
384+
385+ const noUserConfig = {
386+ apps : [
387+ {
388+ serverURL : 'http://localhost:1337/parse' ,
389+ appId : 'testAppId' ,
390+ masterKey : 'testMasterKey' ,
391+ appName : 'TestApp' ,
392+ } ,
393+ ] ,
394+ // No users configured
395+ agent : {
396+ models : [
397+ {
398+ name : 'test-model' ,
399+ provider : 'openai' ,
400+ model : 'gpt-4' ,
401+ apiKey : 'fake-api-key-for-testing' ,
402+ } ,
403+ ] ,
404+ } ,
405+ } ;
406+
407+ beforeAll ( ( done ) => {
408+ const parseDashboard = require ( '../../../Parse-Dashboard/app.js' ) ;
409+ // dev: false to enable the remote access guard
410+ const dashboardApp = parseDashboard ( noUserConfig , {
411+ cookieSessionSecret : SESSION_SECRET ,
412+ } ) ;
413+
414+ const parentApp = express ( ) ;
415+ parentApp . use ( '/' , dashboardApp ) ;
416+
417+ server = parentApp . listen ( 0 , ( ) => {
418+ port = server . address ( ) . port ;
419+ done ( ) ;
420+ } ) ;
421+ } ) ;
422+
423+ afterAll ( ( done ) => {
424+ if ( server ) {
425+ server . close ( done ) ;
426+ } else {
427+ done ( ) ;
428+ }
429+ } ) ;
430+
431+ it ( 'allows local requests to the agent endpoint in no-user mode' , async ( ) => {
432+ // Requests to 127.0.0.1 are local, so they should pass the guard
433+ const res = await makeRequest ( port , {
434+ method : 'POST' ,
435+ path : '/apps/TestApp/agent' ,
436+ body : agentBody ( ) ,
437+ } ) ;
438+ // Should not be blocked by the no-user guard (may fail at CSRF or later)
439+ expect ( res . status ) . not . toBe ( 401 ) ;
440+ } ) ;
441+ } ) ;
442+
443+ describe ( 'Agent endpoint no-user mode — remote requests' , ( ) => {
444+ let server ;
445+ let port ;
446+
447+ const noUserConfig = {
448+ apps : [
449+ {
450+ serverURL : 'http://localhost:1337/parse' ,
451+ appId : 'testAppId' ,
452+ masterKey : 'testMasterKey' ,
453+ appName : 'TestApp' ,
454+ } ,
455+ ] ,
456+ agent : {
457+ models : [
458+ {
459+ name : 'test-model' ,
460+ provider : 'openai' ,
461+ model : 'gpt-4' ,
462+ apiKey : 'fake-api-key-for-testing' ,
463+ } ,
464+ ] ,
465+ } ,
466+ } ;
467+
468+ beforeAll ( ( done ) => {
469+ const parseDashboard = require ( '../../../Parse-Dashboard/app.js' ) ;
470+ const dashboardApp = parseDashboard ( noUserConfig , {
471+ cookieSessionSecret : SESSION_SECRET ,
472+ } ) ;
473+
474+ const parentApp = express ( ) ;
475+ // Spoof a non-local remote address before the dashboard middleware
476+ parentApp . use ( ( req , _res , next ) => {
477+ Object . defineProperty ( req . connection , 'remoteAddress' , {
478+ value : '203.0.113.1' ,
479+ writable : true ,
480+ configurable : true ,
481+ } ) ;
482+ next ( ) ;
483+ } ) ;
484+ parentApp . use ( '/' , dashboardApp ) ;
485+
486+ server = parentApp . listen ( 0 , ( ) => {
487+ port = server . address ( ) . port ;
488+ done ( ) ;
489+ } ) ;
490+ } ) ;
491+
492+ afterAll ( ( done ) => {
493+ if ( server ) {
494+ server . close ( done ) ;
495+ } else {
496+ done ( ) ;
497+ }
498+ } ) ;
499+
500+ it ( 'returns 403 for remote non-HTTPS requests to the agent endpoint' , async ( ) => {
501+ const res = await makeRequest ( port , {
502+ method : 'POST' ,
503+ path : '/apps/TestApp/agent' ,
504+ body : agentBody ( ) ,
505+ } ) ;
506+ expect ( res . status ) . toBe ( 403 ) ;
507+ expect ( res . body . error ) . toBe ( 'Parse Dashboard can only be remotely accessed via HTTPS' ) ;
508+ } ) ;
509+ } ) ;
510+
511+ describe ( 'Agent endpoint no-user mode — remote requests with allowInsecureHTTP' , ( ) => {
512+ let server ;
513+ let port ;
514+
515+ const noUserConfig = {
516+ apps : [
517+ {
518+ serverURL : 'http://localhost:1337/parse' ,
519+ appId : 'testAppId' ,
520+ masterKey : 'testMasterKey' ,
521+ appName : 'TestApp' ,
522+ } ,
523+ ] ,
524+ agent : {
525+ models : [
526+ {
527+ name : 'test-model' ,
528+ provider : 'openai' ,
529+ model : 'gpt-4' ,
530+ apiKey : 'fake-api-key-for-testing' ,
531+ } ,
532+ ] ,
533+ } ,
534+ } ;
535+
536+ beforeAll ( ( done ) => {
537+ const parseDashboard = require ( '../../../Parse-Dashboard/app.js' ) ;
538+ const dashboardApp = parseDashboard ( noUserConfig , {
539+ cookieSessionSecret : SESSION_SECRET ,
540+ allowInsecureHTTP : true ,
541+ } ) ;
542+
543+ const parentApp = express ( ) ;
544+ // Spoof a non-local remote address before the dashboard middleware
545+ parentApp . use ( ( req , _res , next ) => {
546+ Object . defineProperty ( req . connection , 'remoteAddress' , {
547+ value : '203.0.113.1' ,
548+ writable : true ,
549+ configurable : true ,
550+ } ) ;
551+ next ( ) ;
552+ } ) ;
553+ parentApp . use ( '/' , dashboardApp ) ;
554+
555+ server = parentApp . listen ( 0 , ( ) => {
556+ port = server . address ( ) . port ;
557+ done ( ) ;
558+ } ) ;
559+ } ) ;
560+
561+ afterAll ( ( done ) => {
562+ if ( server ) {
563+ server . close ( done ) ;
564+ } else {
565+ done ( ) ;
566+ }
567+ } ) ;
568+
569+ it ( 'returns 401 for remote requests to the agent endpoint in no-user mode when HTTPS is bypassed' , async ( ) => {
570+ const res = await makeRequest ( port , {
571+ method : 'POST' ,
572+ path : '/apps/TestApp/agent' ,
573+ body : agentBody ( ) ,
574+ } ) ;
575+ expect ( res . status ) . toBe ( 401 ) ;
576+ expect ( res . body . error ) . toBe ( 'Configure a user to access Parse Dashboard remotely' ) ;
577+ } ) ;
578+ } ) ;
0 commit comments