@@ -78,14 +78,17 @@ describe("Stream Controller", () => {
7878 publicKey : "GSENDER" ,
7979 } ,
8080 } ;
81+ // Authenticated caller matches body.sender by default (Issue #809).
82+ ( req as any ) . user = { publicKey : 'GSENDER' } ;
8183 res = {
8284 status : vi . fn ( ) . mockReturnThis ( ) ,
8385 json : vi . fn ( ) . mockReturnThis ( ) ,
8486 } ;
8587 } ) ;
8688
87- describe ( "createStream" , ( ) => {
88- it ( "should create a stream successfully" , async ( ) => {
89+ describe ( 'createStream' , ( ) => {
90+ it ( 'should create a stream successfully' , async ( ) => {
91+ ( prisma . stream . findUnique as any ) . mockResolvedValue ( null ) ;
8992 ( prisma . stream . upsert as any ) . mockResolvedValue ( { streamId : 123 } ) ;
9093
9194 await createStream ( req as Request , res as Response ) ;
@@ -94,20 +97,54 @@ describe("Stream Controller", () => {
9497 expect ( prisma . stream . upsert ) . toHaveBeenCalled ( ) ;
9598 } ) ;
9699
97- it ( "should return 400 for invalid streamId" , async ( ) => {
98- req . body . streamId = "abc" ;
100+ it ( 'should return 401 when the request is unauthenticated' , async ( ) => {
101+ ( req as any ) . user = undefined ;
102+
99103 await createStream ( req as Request , res as Response ) ;
100- expect ( res . status ) . toHaveBeenCalledWith ( 400 ) ;
104+
105+ expect ( res . status ) . toHaveBeenCalledWith ( 401 ) ;
106+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
107+ } ) ;
108+
109+ it ( 'should return 403 when the caller is not the body sender (Issue #809)' , async ( ) => {
110+ ( req as any ) . user = { publicKey : 'GATTACKER' } ;
111+
112+ await createStream ( req as Request , res as Response ) ;
113+
114+ expect ( res . status ) . toHaveBeenCalledWith ( 403 ) ;
115+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
101116 } ) ;
102117
103- it ( "should return 400 for non-positive ratePerSecond" , async ( ) => {
104- req . body . ratePerSecond = "0" ;
118+ it ( 'should reject 400 when sender is missing (Issue #809)' , async ( ) => {
119+ delete req . body . sender ;
120+ ( req as any ) . user = { publicKey : 'GSENDER' } ;
121+
105122 await createStream ( req as Request , res as Response ) ;
123+
106124 expect ( res . status ) . toHaveBeenCalledWith ( 400 ) ;
125+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
126+ } ) ;
127+
128+ it ( 'should return 403 and not reactivate a cancelled stream owned by another wallet (Issue #809)' , async ( ) => {
129+ // A victim previously created (and cancelled) this stream.
130+ ( prisma . stream . findUnique as any ) . mockResolvedValue ( {
131+ streamId : 123 ,
132+ sender : 'GVICTIM' ,
133+ isActive : false ,
134+ } ) ;
135+ // Attacker authenticates as themselves and sets sender to their own key,
136+ // trying to hijack the victim's streamId.
137+ req . body . sender = 'GATTACKER' ;
138+ ( req as any ) . user = { publicKey : 'GATTACKER' } ;
139+
140+ await createStream ( req as Request , res as Response ) ;
141+
142+ expect ( res . status ) . toHaveBeenCalledWith ( 403 ) ;
143+ expect ( prisma . stream . upsert ) . not . toHaveBeenCalled ( ) ;
107144 } ) ;
108145
109- it ( " should return 400 for malformed numeric fields" , async ( ) => {
110- req . body . ratePerSecond = " abc" ;
146+ it ( ' should return 400 for invalid streamId' , async ( ) => {
147+ req . body . streamId = ' abc' ;
111148 await createStream ( req as Request , res as Response ) ;
112149 expect ( res . status ) . toHaveBeenCalledWith ( 400 ) ;
113150 } ) ;
0 commit comments