@@ -2078,6 +2078,234 @@ describe("capability cli", () => {
20782078 expectRuntimeErrorContains ( "Video asset at index 0 has neither buffer nor url" ) ;
20792079 } ) ;
20802080
2081+ it ( "fails closed when an url-only generated video exceeds the in-memory byte cap" , async ( ) => {
2082+ mocks . loadConfig . mockReturnValue ( { } ) ;
2083+ mocks . generateVideo . mockResolvedValue ( {
2084+ provider : "vydra" ,
2085+ model : "veo3" ,
2086+ attempts : [ ] ,
2087+ videos : [
2088+ {
2089+ url : "https://example.com/oversized-video.mp4?sig=secret-presigned-token" ,
2090+ mimeType : "video/mp4" ,
2091+ fileName : "provider-name.mp4" ,
2092+ } ,
2093+ ] ,
2094+ } ) ;
2095+ // Offer far more than the 16 MiB default video cap in 1 MiB chunks so the
2096+ // bounded reader has to cancel mid-stream instead of buffering it all. The
2097+ // source would yield 64 MiB if fully drained; a correct guard stops early.
2098+ const oneMiBChunk = new Uint8Array ( 1024 * 1024 ) ;
2099+ const overCapChunks = 64 ;
2100+ let enqueued = 0 ;
2101+ let canceled = false ;
2102+ const oversizedBody = new ReadableStream < Uint8Array > ( {
2103+ pull ( controller ) {
2104+ if ( enqueued >= overCapChunks ) {
2105+ controller . close ( ) ;
2106+ return ;
2107+ }
2108+ enqueued += 1 ;
2109+ controller . enqueue ( oneMiBChunk ) ;
2110+ } ,
2111+ cancel ( ) {
2112+ canceled = true ;
2113+ } ,
2114+ } ) ;
2115+ const fetchMock = vi . fn (
2116+ async ( ) =>
2117+ new Response ( oversizedBody , {
2118+ status : 200 ,
2119+ headers : { "content-type" : "video/mp4" } ,
2120+ } ) ,
2121+ ) ;
2122+ vi . stubGlobal ( "fetch" , fetchMock ) ;
2123+
2124+ await expect (
2125+ runRegisteredCli ( {
2126+ register : registerCapabilityCli as ( program : Command ) => void ,
2127+ // No --output: forces the in-memory buffered fallback path.
2128+ argv : [ "capability" , "video" , "generate" , "--prompt" , "friendly lobster" , "--json" ] ,
2129+ } ) ,
2130+ ) . rejects . toThrow ( "exit 1" ) ;
2131+
2132+ // Real path was driven: the provider URL was actually fetched...
2133+ const fetchCalls = fetchMock . mock . calls as unknown as Array < [ string ] > ;
2134+ expect ( fetchCalls [ 0 ] ?. [ 0 ] ) . toBe (
2135+ "https://example.com/oversized-video.mp4?sig=secret-presigned-token" ,
2136+ ) ;
2137+ // ...and the read was rejected (fail-closed) referencing the provider label
2138+ // and the 16 MiB default cap rather than buffering the body.
2139+ expectRuntimeErrorContains ( "vydra generated video download exceeds 16777216 bytes" ) ;
2140+ // Security regression guard: the overflow error must NOT echo the raw
2141+ // provider URL (it may carry signed/tokenized access material). See the
2142+ // sibling generated-media downloaders, which report provider + cap only.
2143+ expect ( runtimeErrorMessages ( ) . join ( "\n" ) ) . not . toContain ( "secret-presigned-token" ) ;
2144+ expect ( runtimeErrorMessages ( ) . join ( "\n" ) ) . not . toContain ( "https://example.com" ) ;
2145+ // The reader cancelled shortly after crossing the 16 MiB cap rather than
2146+ // draining the full 64 MiB the source was willing to produce.
2147+ expect ( canceled ) . toBe ( true ) ;
2148+ expect ( enqueued ) . toBeLessThan ( overCapChunks ) ;
2149+ expect ( enqueued ) . toBeLessThanOrEqual ( 18 ) ;
2150+ } ) ;
2151+
2152+ it ( "redacts provider video URLs when the no-output download fails" , async ( ) => {
2153+ mocks . loadConfig . mockReturnValue ( { } ) ;
2154+ mocks . generateVideo . mockResolvedValue ( {
2155+ provider : "vydra" ,
2156+ model : "veo3" ,
2157+ attempts : [ ] ,
2158+ videos : [
2159+ {
2160+ url : "https://example.com/private-video.mp4?sig=secret-presigned-token" ,
2161+ mimeType : "video/mp4" ,
2162+ fileName : "provider-name.mp4" ,
2163+ } ,
2164+ ] ,
2165+ } ) ;
2166+ const fetchMock = vi . fn (
2167+ async ( ) =>
2168+ new Response ( "download forbidden" , {
2169+ status : 403 ,
2170+ statusText : "Forbidden" ,
2171+ headers : { "content-type" : "text/plain" } ,
2172+ } ) ,
2173+ ) ;
2174+ vi . stubGlobal ( "fetch" , fetchMock ) ;
2175+
2176+ await expect (
2177+ runRegisteredCli ( {
2178+ register : registerCapabilityCli as ( program : Command ) => void ,
2179+ argv : [ "capability" , "video" , "generate" , "--prompt" , "friendly lobster" , "--json" ] ,
2180+ } ) ,
2181+ ) . rejects . toThrow ( "exit 1" ) ;
2182+
2183+ expectRuntimeErrorContains ( "vydra generated video download failed" ) ;
2184+ expectRuntimeErrorContains ( "HTTP 403" ) ;
2185+ expect ( runtimeErrorMessages ( ) . join ( "\n" ) ) . not . toContain ( "secret-presigned-token" ) ;
2186+ expect ( runtimeErrorMessages ( ) . join ( "\n" ) ) . not . toContain ( "https://example.com" ) ;
2187+ } ) ;
2188+
2189+ it ( "buffers an url-only generated video that stays under the byte cap" , async ( ) => {
2190+ mocks . loadConfig . mockReturnValue ( { } ) ;
2191+ mocks . generateVideo . mockResolvedValue ( {
2192+ provider : "vydra" ,
2193+ model : "veo3" ,
2194+ attempts : [ ] ,
2195+ videos : [
2196+ {
2197+ url : "https://example.com/small-video.mp4" ,
2198+ mimeType : "video/mp4" ,
2199+ fileName : "provider-name.mp4" ,
2200+ } ,
2201+ ] ,
2202+ } ) ;
2203+ const fetchMock = vi . fn (
2204+ async ( ) =>
2205+ new Response ( Buffer . from ( "small-video-bytes" ) , {
2206+ status : 200 ,
2207+ headers : { "content-type" : "video/mp4" } ,
2208+ } ) ,
2209+ ) ;
2210+ vi . stubGlobal ( "fetch" , fetchMock ) ;
2211+
2212+ await runRegisteredCli ( {
2213+ register : registerCapabilityCli as ( program : Command ) => void ,
2214+ // No --output: in-memory buffered fallback path, under cap.
2215+ argv : [ "capability" , "video" , "generate" , "--prompt" , "friendly lobster" , "--json" ] ,
2216+ } ) ;
2217+
2218+ const fetchCalls = fetchMock . mock . calls as unknown as Array < [ string ] > ;
2219+ expect ( fetchCalls [ 0 ] ?. [ 0 ] ) . toBe ( "https://example.com/small-video.mp4" ) ;
2220+ const output = firstJsonOutput ( ) ;
2221+ expect ( output ?. capability ) . toBe ( "video.generate" ) ;
2222+ expect ( output ?. provider ) . toBe ( "vydra" ) ;
2223+ expect ( output ?. outputs as Array < Record < string , unknown > > ) . toHaveLength ( 1 ) ;
2224+ // No overflow error on the under-cap path.
2225+ expect ( runtimeErrorMessages ( ) . join ( "\n" ) ) . not . toContain ( "exceeds" ) ;
2226+ } ) ;
2227+
2228+ it ( "honors a smaller configured mediaMaxMb cap on the in-memory video path" , async ( ) => {
2229+ // Operators can lower the cap via agents.defaults.mediaMaxMb; the bounded
2230+ // read must respect it (here 2 MiB) and cancel even earlier.
2231+ mocks . loadConfig . mockReturnValue ( { agents : { defaults : { mediaMaxMb : 2 } } } ) ;
2232+ mocks . generateVideo . mockResolvedValue ( {
2233+ provider : "vydra" ,
2234+ model : "veo3" ,
2235+ attempts : [ ] ,
2236+ videos : [
2237+ {
2238+ url : "https://example.com/over-2mb-video.mp4" ,
2239+ mimeType : "video/mp4" ,
2240+ fileName : "provider-name.mp4" ,
2241+ } ,
2242+ ] ,
2243+ } ) ;
2244+ const oneMiBChunk = new Uint8Array ( 1024 * 1024 ) ;
2245+ const totalChunks = 16 ;
2246+ let enqueued = 0 ;
2247+ const body = new ReadableStream < Uint8Array > ( {
2248+ pull ( controller ) {
2249+ if ( enqueued >= totalChunks ) {
2250+ controller . close ( ) ;
2251+ return ;
2252+ }
2253+ enqueued += 1 ;
2254+ controller . enqueue ( oneMiBChunk ) ;
2255+ } ,
2256+ } ) ;
2257+ const fetchMock = vi . fn (
2258+ async ( ) => new Response ( body , { status : 200 , headers : { "content-type" : "video/mp4" } } ) ,
2259+ ) ;
2260+ vi . stubGlobal ( "fetch" , fetchMock ) ;
2261+
2262+ await expect (
2263+ runRegisteredCli ( {
2264+ register : registerCapabilityCli as ( program : Command ) => void ,
2265+ argv : [ "capability" , "video" , "generate" , "--prompt" , "friendly lobster" , "--json" ] ,
2266+ } ) ,
2267+ ) . rejects . toThrow ( "exit 1" ) ;
2268+
2269+ // Cap resolved from config (2 MiB = 2097152), not the 16 MiB default.
2270+ expectRuntimeErrorContains ( "vydra generated video download exceeds 2097152 bytes" ) ;
2271+ // Cancelled after crossing 2 MiB, far below the 16 MiB the source offered.
2272+ expect ( enqueued ) . toBeLessThanOrEqual ( 4 ) ;
2273+ } ) ;
2274+
2275+ it ( "buffers an empty-body url-only generated video without error" , async ( ) => {
2276+ // Boundary: a 0-byte body is trivially under the cap and must not error.
2277+ mocks . loadConfig . mockReturnValue ( { } ) ;
2278+ mocks . generateVideo . mockResolvedValue ( {
2279+ provider : "vydra" ,
2280+ model : "veo3" ,
2281+ attempts : [ ] ,
2282+ videos : [
2283+ {
2284+ url : "https://example.com/empty-video.mp4" ,
2285+ mimeType : "video/mp4" ,
2286+ fileName : "provider-name.mp4" ,
2287+ } ,
2288+ ] ,
2289+ } ) ;
2290+ const fetchMock = vi . fn (
2291+ async ( ) =>
2292+ new Response ( Buffer . alloc ( 0 ) , {
2293+ status : 200 ,
2294+ headers : { "content-type" : "video/mp4" } ,
2295+ } ) ,
2296+ ) ;
2297+ vi . stubGlobal ( "fetch" , fetchMock ) ;
2298+
2299+ await runRegisteredCli ( {
2300+ register : registerCapabilityCli as ( program : Command ) => void ,
2301+ argv : [ "capability" , "video" , "generate" , "--prompt" , "friendly lobster" , "--json" ] ,
2302+ } ) ;
2303+
2304+ const output = firstJsonOutput ( ) ;
2305+ expect ( output ?. capability ) . toBe ( "video.generate" ) ;
2306+ expect ( runtimeErrorMessages ( ) . join ( "\n" ) ) . not . toContain ( "exceeds" ) ;
2307+ } ) ;
2308+
20812309 it ( "rejects partial image generate count before provider dispatch" , async ( ) => {
20822310 await expect (
20832311 runRegisteredCli ( {
0 commit comments