@@ -572,3 +572,102 @@ func TestIsImageType(t *testing.T) {
572572 }
573573 }
574574}
575+
576+ func TestEnsureRunning_SecretReference (t * testing.T ) {
577+ mock := ssh .NewMockExecutor ("1.2.3.4" ,
578+ // No stored credentials.
579+ ssh.MockCommand {Match : "cat /deployments/myapp/accessories/nucleus/credentials" , Err : fmt .Errorf ("not found" )},
580+ // Secret exists and decrypts.
581+ ssh.MockCommand {Match : "test -f /deployments/myapp/secrets/NUCLEUS_PASSWORD.age" , Output : "" },
582+ ssh.MockCommand {Match : "age -d -i /deployments/.age-key /deployments/myapp/secrets/NUCLEUS_PASSWORD.age" , Output : "s3cr3t-pa$$word\n " },
583+ // Not running.
584+ ssh.MockCommand {Match : "docker inspect" , Err : fmt .Errorf ("not found" )},
585+ ssh.MockCommand {Match : "mkdir -p /deployments/myapp/accessories/nucleus" , Output : "" },
586+ ssh.MockCommand {Match : "docker run" , Output : "abc123" },
587+ )
588+
589+ var buf bytes.Buffer
590+ mgr := NewManager (mock , & buf )
591+
592+ _ , err := mgr .EnsureRunning (context .Background (), "myapp" , "nucleus" , config.AccessoryConfig {
593+ Image : "ghcr.io/neutron-build/nucleus:latest" ,
594+ Port : 5432 ,
595+ Env : map [string ]string {
596+ "NUCLEUS_PASSWORD" : "secret:NUCLEUS_PASSWORD" ,
597+ "NUCLEUS_PLAIN" : "literal" ,
598+ },
599+ })
600+ if err != nil {
601+ t .Fatalf ("EnsureRunning: %v" , err )
602+ }
603+
604+ var runCmd string
605+ for _ , call := range mock .Calls {
606+ if strings .HasPrefix (call , "docker run" ) {
607+ runCmd = call
608+ }
609+ }
610+ if runCmd == "" {
611+ t .Fatal ("expected docker run command" )
612+ }
613+ // The decrypted value is injected (shell-quoted), not the reference.
614+ if ! strings .Contains (runCmd , `'NUCLEUS_PASSWORD=s3cr3t-pa$$word'` ) {
615+ t .Errorf ("expected decrypted secret in docker run env, got: %s" , runCmd )
616+ }
617+ if strings .Contains (runCmd , "secret:NUCLEUS_PASSWORD" ) {
618+ t .Errorf ("secret reference leaked into docker run: %s" , runCmd )
619+ }
620+ if ! strings .Contains (runCmd , `'NUCLEUS_PLAIN=literal'` ) {
621+ t .Errorf ("literal env should pass through, got: %s" , runCmd )
622+ }
623+
624+ // The plaintext must never be persisted to the credentials file.
625+ for path , content := range mock .Files {
626+ if strings .Contains (path , "credentials" ) && strings .Contains (string (content ), "s3cr3t" ) {
627+ t .Errorf ("secret value persisted in plaintext to %s" , path )
628+ }
629+ }
630+ }
631+
632+ func TestEnsureRunning_SecretReferenceMissing (t * testing.T ) {
633+ mock := ssh .NewMockExecutor ("1.2.3.4" ,
634+ ssh.MockCommand {Match : "cat /deployments/myapp/accessories/nucleus/credentials" , Err : fmt .Errorf ("not found" )},
635+ // Secret file does not exist.
636+ ssh.MockCommand {Match : "test -f /deployments/myapp/secrets/NUCLEUS_PASSWORD.age" , Err : fmt .Errorf ("exit 1" )},
637+ )
638+
639+ var buf bytes.Buffer
640+ mgr := NewManager (mock , & buf )
641+
642+ _ , err := mgr .EnsureRunning (context .Background (), "myapp" , "nucleus" , config.AccessoryConfig {
643+ Image : "ghcr.io/neutron-build/nucleus:latest" ,
644+ Env : map [string ]string {"NUCLEUS_PASSWORD" : "secret:NUCLEUS_PASSWORD" },
645+ })
646+ if err == nil {
647+ t .Fatal ("expected error for unset secret" )
648+ }
649+ if ! strings .Contains (err .Error (), "teploy secret set NUCLEUS_PASSWORD=" ) {
650+ t .Errorf ("error should tell the user how to set the secret, got: %v" , err )
651+ }
652+ // Must fail before any container is started.
653+ for _ , call := range mock .Calls {
654+ if strings .HasPrefix (call , "docker run" ) {
655+ t .Error ("container must not start when a secret reference is unresolvable" )
656+ }
657+ }
658+ }
659+
660+ func TestEnsureRunning_SecretReferenceEmptyKey (t * testing.T ) {
661+ mock := ssh .NewMockExecutor ("1.2.3.4" ,
662+ ssh.MockCommand {Match : "cat /deployments/myapp/accessories/nucleus/credentials" , Err : fmt .Errorf ("not found" )},
663+ )
664+ var buf bytes.Buffer
665+ mgr := NewManager (mock , & buf )
666+ _ , err := mgr .EnsureRunning (context .Background (), "myapp" , "nucleus" , config.AccessoryConfig {
667+ Image : "nucleus:latest" ,
668+ Env : map [string ]string {"NUCLEUS_PASSWORD" : "secret:" },
669+ })
670+ if err == nil || ! strings .Contains (err .Error (), "empty secret reference" ) {
671+ t .Fatalf ("expected empty-reference error, got: %v" , err )
672+ }
673+ }
0 commit comments