@@ -62,6 +62,8 @@ describe('Stream Controller', () => {
6262 query : { } ,
6363 params : { } ,
6464 } ;
65+ // Authenticated caller matches body.sender by default (Issue #809).
66+ ( req as any ) . user = { publicKey : 'GSENDER' } ;
6567 res = {
6668 status : vi . fn ( ) . mockReturnThis ( ) ,
6769 json : vi . fn ( ) . mockReturnThis ( ) ,
@@ -70,6 +72,7 @@ describe('Stream Controller', () => {
7072
7173 describe ( 'createStream' , ( ) => {
7274 it ( 'should create a stream successfully' , async ( ) => {
75+ ( prisma . stream . findUnique as any ) . mockResolvedValue ( null ) ;
7376 ( prisma . stream . upsert as any ) . mockResolvedValue ( { streamId : 123 } ) ;
7477
7578 await createStream ( req as Request , res as Response ) ;
@@ -78,6 +81,52 @@ describe('Stream Controller', () => {
7881 expect ( prisma . stream . upsert ) . toHaveBeenCalled ( ) ;
7982 } ) ;
8083
84+ it ( 'should return 401 when the request is unauthenticated' , async ( ) => {
85+ ( req as any ) . user = undefined ;
86+
87+ await createStream ( req as Request , res as Response ) ;
88+
89+ expect ( res . status ) . toHaveBeenCalledWith ( 401 ) ;
90+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
91+ } ) ;
92+
93+ it ( 'should return 403 when the caller is not the body sender (Issue #809)' , async ( ) => {
94+ ( req as any ) . user = { publicKey : 'GATTACKER' } ;
95+
96+ await createStream ( req as Request , res as Response ) ;
97+
98+ expect ( res . status ) . toHaveBeenCalledWith ( 403 ) ;
99+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
100+ } ) ;
101+
102+ it ( 'should reject 400 when sender is missing (Issue #809)' , async ( ) => {
103+ delete req . body . sender ;
104+ ( req as any ) . user = { publicKey : 'GSENDER' } ;
105+
106+ await createStream ( req as Request , res as Response ) ;
107+
108+ expect ( res . status ) . toHaveBeenCalledWith ( 400 ) ;
109+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
110+ } ) ;
111+
112+ it ( 'should return 403 and not reactivate a cancelled stream owned by another wallet (Issue #809)' , async ( ) => {
113+ // A victim previously created (and cancelled) this stream.
114+ ( prisma . stream . findUnique as any ) . mockResolvedValue ( {
115+ streamId : 123 ,
116+ sender : 'GVICTIM' ,
117+ isActive : false ,
118+ } ) ;
119+ // Attacker authenticates as themselves and sets sender to their own key,
120+ // trying to hijack the victim's streamId.
121+ req . body . sender = 'GATTACKER' ;
122+ ( req as any ) . user = { publicKey : 'GATTACKER' } ;
123+
124+ await createStream ( req as Request , res as Response ) ;
125+
126+ expect ( res . status ) . toHaveBeenCalledWith ( 403 ) ;
127+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
128+ } ) ;
129+
81130 it ( 'should return 400 for invalid streamId' , async ( ) => {
82131 req . body . streamId = 'abc' ;
83132 await createStream ( req as Request , res as Response ) ;
0 commit comments