@@ -7,7 +7,7 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
77import { captureInvocation , type InvocationInput , type Phase } from '../agents/contract.ts' ;
88import { AGENT_IMAGE , assertBuiltAgentImage , buildAgentImage } from '../agents/container/image.ts' ;
99import { createContainerProfile , disposeContainerProfile } from '../agents/container/profile.ts' ;
10- import { createValidatedContainer , prepareTaskFilesystems , removeTaskFilesystems , runContainer ,
10+ import { createValidatedContainer , prepareTaskFilesystems , removeTaskFilesystems , runContainer , startValidatedContainer ,
1111 hasExactOptions , validateContainer } from '../agents/container/run.ts' ;
1212import { createTaskClone } from '../git/clone.ts' ;
1313import { createVendorNetwork , removeVendorNetwork , type VendorNetwork } from '../agents/network/network.ts' ;
@@ -96,6 +96,13 @@ describe('real Docker agent isolation', () => {
9696 expect ( runContainer ( profile ( data , phase , 'phase-worktree' ) ) ) . toBe ( '' ) ;
9797 } , 60_000 ) ;
9898
99+ it ( 'removes the invocation proxy and network after the container settles' , ( ) => {
100+ const data = fixture ( ) , valid = profile ( data , 'planning' , 'noop' ) ;
101+ expect ( runContainer ( valid ) ) . toBe ( '' ) ;
102+ expect ( spawnSync ( 'docker' , [ 'container' , 'inspect' , valid . network . proxyContainer ] ) . status ) . not . toBe ( 0 ) ;
103+ expect ( spawnSync ( 'docker' , [ 'network' , 'inspect' , valid . network . name ] ) . status ) . not . toBe ( 0 ) ;
104+ } , 60_000 ) ;
105+
99106 it ( 'runs read-only with no root capabilities, host paths, inherited secrets, or writable tools' , ( ) => {
100107 const data = fixture ( ) ;
101108 process . env . HOST_SECRET_SENTINEL = 'must-not-reach-container' ;
@@ -230,10 +237,10 @@ describe('real Docker agent isolation', () => {
230237
231238 it ( 'does not remove an active container when a duplicate attempt name collides' , ( ) => {
232239 const data = fixture ( ) , captured = invocation ( data . clone , 'planning' ) ;
233- const common = governed ( captured ) ;
234- const first = createContainerProfile ( { ...common , filesystems : data . filesystems ,
240+ const duplicateInvocation = captureInvocation ( { ... captured } ) ;
241+ const first = createContainerProfile ( { ...governed ( captured ) , filesystems : data . filesystems ,
235242 inputDirectory : data . input , codexAuthFile : data . fakeAuth , imageId } ) ;
236- const duplicate = createContainerProfile ( { ...common , filesystems : data . filesystems ,
243+ const duplicate = createContainerProfile ( { ...governed ( duplicateInvocation ) , filesystems : data . filesystems ,
237244 inputDirectory : data . input , codexAuthFile : data . fakeAuth , imageId } ) ;
238245 profiles . push ( first , duplicate ) ;
239246 docker ( ...first . args ) ; containers . add ( first . name ) ;
@@ -274,6 +281,14 @@ describe('real Docker agent isolation', () => {
274281 expect ( ( ) => validateContainer ( valid . name , valid ) ) . toThrow ( 'DNS configuration' ) ;
275282 docker ( 'rm' , '--force' , valid . name ) ; containers . delete ( valid . name ) ;
276283
284+ for ( const extra of [ [ '--add-host=api.openai.com:127.0.0.1' ] , [ '--publish=127.0.0.1::3128' ] ] ) {
285+ const changedArgs = [ ...valid . args . slice ( 0 , valid . args . indexOf ( imageId ) ) , ...extra ,
286+ ...valid . args . slice ( valid . args . indexOf ( imageId ) ) ] ;
287+ docker ( ...changedArgs ) ; containers . add ( valid . name ) ;
288+ expect ( ( ) => validateContainer ( valid . name , valid ) ) . toThrow ( 'host or port configuration' ) ;
289+ docker ( 'rm' , '--force' , valid . name ) ; containers . delete ( valid . name ) ;
290+ }
291+
277292 const imageIndex = valid . args . indexOf ( imageId ) ;
278293 const namespaceArgs = [ ...valid . args . slice ( 0 , imageIndex ) , '--uts=host' , ...valid . args . slice ( imageIndex ) ] ;
279294 docker ( ...namespaceArgs ) ; containers . add ( valid . name ) ;
@@ -292,10 +307,23 @@ describe('real Docker agent isolation', () => {
292307 try {
293308 docker ( 'run' , '--detach' , '--name' , rogue , `--network=${ valid . network . name } ` , '--entrypoint' , 'node' , imageId ,
294309 '-e' , 'setInterval(()=>{},1000)' ) ;
295- expect ( ( ) => createValidatedContainer ( valid ) ) . toThrow ( 'network or proxy changed ' ) ;
310+ expect ( ( ) => createValidatedContainer ( valid ) ) . toThrow ( 'cleanup did not settle ' ) ;
296311 const absent = spawnSync ( 'docker' , [ 'container' , 'inspect' , valid . name ] , { encoding : 'utf8' } ) ;
297312 expect ( absent . status ) . not . toBe ( 0 ) ;
298- } finally { spawnSync ( 'docker' , [ 'rm' , '--force' , rogue ] , { stdio : 'ignore' } ) ; }
313+ } finally {
314+ spawnSync ( 'docker' , [ 'rm' , '--force' , rogue ] , { stdio : 'ignore' } ) ;
315+ disposeContainerProfile ( valid ) ;
316+ }
317+ } , 60_000 ) ;
318+
319+ it ( 'revalidates the agent attachment immediately before start' , ( ) => {
320+ const data = fixture ( ) , valid = profile ( data , 'planning' , 'must-not-run' ) ;
321+ createValidatedContainer ( valid ) ; containers . add ( valid . name ) ;
322+ docker ( 'network' , 'disconnect' , valid . network . name , valid . name ) ;
323+ docker ( 'network' , 'connect' , 'bridge' , valid . name ) ;
324+ expect ( ( ) => startValidatedContainer ( valid ) ) . toThrow ( / n e t w o r k a t t a c h m e n t | l o c k d o w n / ) ;
325+ containers . delete ( valid . name ) ;
326+ expect ( spawnSync ( 'docker' , [ 'container' , 'inspect' , valid . name ] ) . status ) . not . toBe ( 0 ) ;
299327 } , 60_000 ) ;
300328
301329 it ( 'creates containers from the captured immutable image rather than its mutable tag' , ( ) => {
0 commit comments