@@ -248,6 +248,42 @@ describe('ResolvedSecretTraceRegistry', () => {
248248 }
249249 } )
250250
251+ it ( 'keeps a named anonymous secret distinct from anonymous provenance' , async ( ) => {
252+ mockDecryptSecret . mockResolvedValueOnce ( { decrypted : 'same-secret' } )
253+ const registry = new ResolvedSecretTraceRegistry (
254+ [ { name : 'anonymous' , plaintext : 'same-secret' , encryptedValue : 'shared-ciphertext' } ] ,
255+ { userId : 'user-1' , workspaceId : 'workspace-1' }
256+ )
257+ registry . recordResolved ( 'anonymous' , 'same-secret' )
258+ await registry . importProvenance (
259+ {
260+ version : 1 ,
261+ complete : true ,
262+ entries : [ { encryptedValue : 'shared-ciphertext' } ] ,
263+ scope : { userId : 'user-1' , workspaceId : 'workspace-1' } ,
264+ } ,
265+ { trusted : true , anonymous : true }
266+ )
267+
268+ expect ( registry . exportCommittedProvenanceForValue ( 'same-secret' ) ) . toEqual ( {
269+ version : 1 ,
270+ complete : true ,
271+ entries : [
272+ { encryptedValue : 'shared-ciphertext' } ,
273+ { name : 'anonymous' , encryptedValue : 'shared-ciphertext' } ,
274+ ] ,
275+ scope : { userId : 'user-1' , workspaceId : 'workspace-1' } ,
276+ } )
277+ const snapshot = registry . getModelEgressSnapshot ( )
278+ expect ( snapshot . complete ) . toBe ( true )
279+ if ( snapshot . complete ) {
280+ expect ( snapshot . matches ) . toContainEqual ( {
281+ plaintext : 'same-secret' ,
282+ replacement : ANONYMOUS_SECRET_TRACE_REPLACEMENT ,
283+ } )
284+ }
285+ } )
286+
251287 it ( 'projects committed provenance while temporary activations are pending' , ( ) => {
252288 const registry = new ResolvedSecretTraceRegistry ( [
253289 { name : 'API_KEY' , plaintext : 'secret-value' , encryptedValue : 'encrypted-value' } ,
@@ -701,6 +737,102 @@ describe('ResolvedSecretTraceRegistry', () => {
701737 } )
702738 } )
703739
740+ it ( 'exports active provenance for a registered legacy runtime alias' , ( ) => {
741+ const registry = new ResolvedSecretTraceRegistry ( [
742+ { name : 'API-KEY' , plaintext : 'secret-value' , encryptedValue : 'present-ciphertext' } ,
743+ ] )
744+ registry . recordResolved ( 'API-KEY' , 'secret-value' )
745+
746+ expect ( registry . exportCommittedProvenanceForValue ( 'prefix __var_API_KEY suffix' ) ) . toEqual ( {
747+ version : 1 ,
748+ complete : true ,
749+ entries : [ { name : 'API-KEY' , encryptedValue : 'present-ciphertext' } ] ,
750+ } )
751+ } )
752+
753+ it ( 'ignores repeated unrelated runtime aliases without exhausting the scan budget' , ( ) => {
754+ const registry = new ResolvedSecretTraceRegistry ( [
755+ { name : 'API_KEY' , plaintext : 'secret-value' , encryptedValue : 'present-ciphertext' } ,
756+ ] )
757+ registry . recordResolved ( 'API_KEY' , 'secret-value' )
758+
759+ expect (
760+ registry . exportCommittedProvenanceForValue ( `${ '__var_Z ' . repeat ( 1_000_001 ) } __var_API_KEY` )
761+ ) . toEqual ( {
762+ version : 1 ,
763+ complete : true ,
764+ entries : [ { name : 'API_KEY' , encryptedValue : 'present-ciphertext' } ] ,
765+ } )
766+ } )
767+
768+ it ( 'matches legacy runtime aliases as complete tokens instead of prefixes' , ( ) => {
769+ const registry = new ResolvedSecretTraceRegistry ( [
770+ { name : 'A' , plaintext : 'secret-a' , encryptedValue : 'ciphertext-a' } ,
771+ { name : 'API_KEY' , plaintext : 'secret-api' , encryptedValue : 'ciphertext-api' } ,
772+ ] )
773+ registry . recordResolved ( 'A' , 'secret-a' )
774+ registry . recordResolved ( 'API_KEY' , 'secret-api' )
775+
776+ expect ( registry . exportCommittedProvenanceForValue ( '__var_API_KEY' ) ) . toEqual ( {
777+ version : 1 ,
778+ complete : true ,
779+ entries : [ { name : 'API_KEY' , encryptedValue : 'ciphertext-api' } ] ,
780+ } )
781+ } )
782+
783+ it ( 'conservatively retains every secret mapped to a colliding runtime alias' , ( ) => {
784+ const registry = new ResolvedSecretTraceRegistry ( [
785+ { name : 'API-KEY' , plaintext : 'first-secret' , encryptedValue : 'first-ciphertext' } ,
786+ { name : 'API_KEY' , plaintext : 'second-secret' , encryptedValue : 'second-ciphertext' } ,
787+ ] )
788+ registry . recordResolved ( 'API-KEY' , 'first-secret' )
789+ registry . recordResolved ( 'API_KEY' , 'second-secret' )
790+
791+ expect ( registry . exportCommittedProvenanceForValue ( '__var_API_KEY' ) ) . toEqual ( {
792+ version : 1 ,
793+ complete : true ,
794+ entries : [
795+ { name : 'API-KEY' , encryptedValue : 'first-ciphertext' } ,
796+ { name : 'API_KEY' , encryptedValue : 'second-ciphertext' } ,
797+ ] ,
798+ } )
799+ } )
800+
801+ it ( 'retains an alias-specific entry when multiple names share one plaintext' , ( ) => {
802+ const registry = new ResolvedSecretTraceRegistry ( [
803+ { name : 'FIRST' , plaintext : 'shared-secret' , encryptedValue : 'first-ciphertext' } ,
804+ { name : 'SECOND' , plaintext : 'shared-secret' , encryptedValue : 'second-ciphertext' } ,
805+ ] )
806+ registry . recordResolved ( 'FIRST' , 'shared-secret' )
807+ registry . recordResolved ( 'SECOND' , 'shared-secret' )
808+
809+ expect ( registry . exportCommittedProvenanceForValue ( '__var_SECOND' ) ) . toEqual ( {
810+ version : 1 ,
811+ complete : true ,
812+ entries : [ { name : 'SECOND' , encryptedValue : 'second-ciphertext' } ] ,
813+ } )
814+ } )
815+
816+ it ( 'conservatively retains every active secret that shares a raw plaintext literal' , ( ) => {
817+ const registry = new ResolvedSecretTraceRegistry ( [
818+ { name : 'FIRST' , plaintext : 'true' , encryptedValue : 'first-ciphertext' } ,
819+ { name : 'SECOND' , plaintext : 'true' , encryptedValue : 'second-ciphertext' } ,
820+ ] )
821+ registry . recordResolved ( 'FIRST' , 'true' )
822+ registry . recordResolved ( 'SECOND' , 'true' )
823+
824+ const expected = {
825+ version : 1 as const ,
826+ complete : true ,
827+ entries : [
828+ { name : 'FIRST' , encryptedValue : 'first-ciphertext' } ,
829+ { name : 'SECOND' , encryptedValue : 'second-ciphertext' } ,
830+ ] ,
831+ }
832+ expect ( registry . exportCommittedProvenanceForValue ( 'true' ) ) . toEqual ( expected )
833+ expect ( registry . exportCommittedProvenanceForValue ( true ) ) . toEqual ( expected )
834+ } )
835+
704836 it ( 'exports active numeric, boolean, and null literals crossing a value boundary' , ( ) => {
705837 const registry = new ResolvedSecretTraceRegistry ( [
706838 { name : 'NUMBER' , plaintext : '1234' , encryptedValue : 'number-ciphertext' } ,
0 commit comments