@@ -118,6 +118,29 @@ func ExecuteVaultTest(t *testing.T, testEnv *ttypes.TestEnvironment) {
118118 executeVaultSecretsDeleteTest (t , secretID , owner , gwURL , []string {"alt" }, sc , wfReg )
119119 executeVaultSecretsGetNotFoundViaWorkflowTest (t , subEnv , "bdela1" , secretID , "alt" , ulCh , bmCh )
120120 })
121+
122+ t .Run ("identifier_validation" , func (t * testing.T ) {
123+ if parallelEnabled {
124+ t .Parallel ()
125+ }
126+ subEnv := t_helpers .SetupTestEnvironmentWithPerTestKeys (t , testEnv .TestConfig )
127+ sc := subEnv .CreEnvironment .Blockchains [0 ].(* evm.Blockchain ).SethClient
128+ owner := sc .MustGetRootKeyAddress ().Hex ()
129+ wfRegAddr := crecontracts .MustGetAddressFromDataStore (subEnv .CreEnvironment .CldfEnvironment .DataStore , subEnv .CreEnvironment .Blockchains [0 ].ChainSelector (), keystone_changeset .WorkflowRegistry .String (), subEnv .CreEnvironment .ContractVersions [keystone_changeset .WorkflowRegistry .String ()], "" )
130+ wfReg , err := workflow_registry_v2_wrapper .NewWorkflowRegistry (common .HexToAddress (wfRegAddr ), sc .Client )
131+ require .NoError (t , err )
132+ require .NoError (t , creworkflow .LinkOwner (sc , common .HexToAddress (wfRegAddr ), subEnv .CreEnvironment .ContractVersions [keystone_changeset .WorkflowRegistry .String ()]))
133+ ulCh := make (chan * workflowevents.UserLogs , 1000 )
134+ bmCh := make (chan * commonevents.BaseMessage , 1000 )
135+ sink := t_helpers .StartChipTestSink (t , t_helpers .GetPublishFn (testLogger , ulCh , bmCh ))
136+ t .Cleanup (func () {
137+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
138+ defer cancel ()
139+ t_helpers .ShutdownChipSinkWithDrain (ctx , sink , ulCh , bmCh )
140+ })
141+ executeVaultSecretsIdentifierValidationTest (t , owner , gwURL , sc , wfReg )
142+ executeVaultSecretsGetInvalidIdentifierViaWorkflowTest (t , subEnv , "vget1" , ulCh , bmCh )
143+ })
121144}
122145
123146func executeVaultSecretsCreateTest (t * testing.T , encryptedSecret , secretID , owner , gatewayURL string , namespaces []string , sethClient * seth.Client , wfRegistryContract * workflow_registry_v2_wrapper.WorkflowRegistry ) {
@@ -619,3 +642,118 @@ func allowlistRequest(t *testing.T, owner string, request jsonrpc.Request[json.R
619642 framework .L .Info ().Msgf ("Allowlisted request digestHexStr: %s, owner: %s, expiry: %d" , hex .EncodeToString (req .RequestDigest [:]), req .Owner .Hex (), req .ExpiryTimestamp )
620643 }
621644}
645+
646+ func executeVaultSecretsGetInvalidIdentifierViaWorkflowTest (
647+ t * testing.T , testEnv * ttypes.TestEnvironment ,
648+ workflowBaseName string ,
649+ userLogsCh chan * workflowevents.UserLogs , baseMessageCh chan * commonevents.BaseMessage ,
650+ ) {
651+ testLogger := framework .L
652+ testLogger .Info ().Msg ("Verifying get secret is rejected for invalid identifier via workflow..." )
653+
654+ const workflowFileLocation = "./vaultsecret/main.go"
655+
656+ workflowName := t_helpers .UniqueWorkflowName (testEnv , workflowBaseName )
657+ workflowID := t_helpers .CompileAndDeployWorkflow (t , testEnv , testLogger , workflowName , & vaultsecret_config.Config {
658+ SecretKey : "invalid-key-with-hyphens" , // hyphen not in [a-zA-Z0-9_]; tests invalid key
659+ SecretNamespace : "main" ,
660+ SecretKey2 : "validkey" ,
661+ SecretNamespace2 : "invalid-namespace-with-hyphens" , // hyphen not in [a-zA-Z0-9_]; tests invalid namespace
662+ ExpectInvalidIdentifier : true ,
663+ }, workflowFileLocation )
664+
665+ // Both invalid-key and invalid-namespace checks run in the same cron trigger; a single
666+ // success log is emitted only after both GetSecret calls are correctly rejected.
667+ t_helpers .WatchWorkflowLogs (t , testLogger , userLogsCh , baseMessageCh , t_helpers .WorkflowEngineInitErrorLog ,
668+ "Vault get correctly rejected invalid identifier" , 4 * time .Minute , t_helpers .WithUserLogWorkflowID (workflowID ))
669+ testLogger .Info ().Msg ("Vault get invalid identifier via workflow test completed" )
670+ }
671+
672+ // executeVaultSecretsIdentifierValidationTest verifies that the gateway rejects requests whose
673+ // secret identifiers contain characters outside the allowed alphanumeric+underscore set.
674+ // All four management request types (create, update, delete, list) are exercised across
675+ // invalid key, invalid namespace, and invalid owner cases. Positive-path coverage is provided
676+ // by basic_crud; this test focuses only on rejection behaviour.
677+ func executeVaultSecretsIdentifierValidationTest (t * testing.T , owner , gatewayURL string , sethClient * seth.Client , wfRegistryContract * workflow_registry_v2_wrapper.WorkflowRegistry ) {
678+ t .Helper ()
679+
680+ const (
681+ validKey = "validkey"
682+ invalidKey = "invalid-key-with-hyphens" // hyphen not in [a-zA-Z0-9_]
683+ validNamespace = "main"
684+ invalidNamespace = "invalid-namespace-hyphens" // hyphen not in [a-zA-Z0-9_]
685+ invalidOwner = "invalid-owner-with-hyphens" // hyphen not in [a-zA-Z0-9_]
686+ )
687+ // dummyEncrypted is a placeholder; identifier validation fires before ciphertext checks.
688+ const dummyEncrypted = "deadbeef"
689+
690+ sendWriteAndAssert := func (t * testing.T , method , caseName string , secret * vault_helpers.EncryptedSecret ) {
691+ t .Helper ()
692+ uniqueRequestID := uuid .New ().String ()
693+ var body []byte
694+ var err error
695+ switch method {
696+ case vaulttypes .MethodSecretsCreate :
697+ body , err = json .Marshal (vault_helpers.CreateSecretsRequest {RequestId : uniqueRequestID , EncryptedSecrets : []* vault_helpers.EncryptedSecret {secret }})
698+ case vaulttypes .MethodSecretsUpdate :
699+ body , err = json .Marshal (vault_helpers.UpdateSecretsRequest {RequestId : uniqueRequestID , EncryptedSecrets : []* vault_helpers.EncryptedSecret {secret }})
700+ case vaulttypes .MethodSecretsDelete :
701+ body , err = json .Marshal (vault_helpers.DeleteSecretsRequest {RequestId : uniqueRequestID , Ids : []* vault_helpers.SecretIdentifier {secret .Id }})
702+ }
703+ require .NoError (t , err )
704+ bodyJSON := json .RawMessage (body )
705+ req := jsonrpc.Request [json.RawMessage ]{Version : jsonrpc .JsonRpcVersion , ID : uniqueRequestID , Method : method , Params : & bodyJSON }
706+ allowlistRequest (t , owner , req , sethClient , wfRegistryContract )
707+ reqBody , err := json .Marshal (req )
708+ require .NoError (t , err )
709+ _ , respBody := sendVaultRequestToGateway (t , gatewayURL , reqBody )
710+ require .Contains (t , string (respBody ), "alphanumeric" , "[%s] expected alphanumeric rejection for %s" , method , caseName )
711+ framework .L .Info ().Msgf ("[%s] %s correctly rejected: %s" , method , caseName , string (respBody ))
712+ }
713+
714+ type writeCase struct {
715+ name string
716+ key , own , ns string
717+ }
718+ writeCases := []writeCase {
719+ {"invalid key" , invalidKey , owner , validNamespace },
720+ {"invalid namespace" , validKey , owner , invalidNamespace },
721+ {"invalid owner" , validKey , invalidOwner , validNamespace },
722+ }
723+
724+ for _ , op := range []string {vaulttypes .MethodSecretsCreate , vaulttypes .MethodSecretsUpdate , vaulttypes .MethodSecretsDelete } {
725+ framework .L .Info ().Msgf ("Testing identifier validation for %s request..." , op )
726+ for _ , tc := range writeCases {
727+ sendWriteAndAssert (t , op , tc .name , & vault_helpers.EncryptedSecret {
728+ Id : & vault_helpers.SecretIdentifier {Key : tc .key , Owner : tc .own , Namespace : tc .ns },
729+ EncryptedValue : dummyEncrypted ,
730+ })
731+ }
732+ }
733+
734+ type listCase struct {
735+ name string
736+ own , ns string
737+ }
738+ listCases := []listCase {
739+ {"invalid namespace" , owner , invalidNamespace },
740+ {"invalid owner" , invalidOwner , validNamespace },
741+ }
742+
743+ framework .L .Info ().Msg ("Testing identifier validation for list request..." )
744+ for _ , tc := range listCases {
745+ uniqueRequestID := uuid .New ().String ()
746+ body , err := json .Marshal (vault_helpers.ListSecretIdentifiersRequest {RequestId : uniqueRequestID , Owner : tc .own , Namespace : tc .ns })
747+ require .NoError (t , err )
748+ bodyJSON := json .RawMessage (body )
749+ req := jsonrpc.Request [json.RawMessage ]{Version : jsonrpc .JsonRpcVersion , ID : uniqueRequestID , Method : vaulttypes .MethodSecretsList , Params : & bodyJSON }
750+ allowlistRequest (t , owner , req , sethClient , wfRegistryContract )
751+ reqBody , err := json .Marshal (req )
752+ require .NoError (t , err )
753+ _ , respBody := sendVaultRequestToGateway (t , gatewayURL , reqBody )
754+ require .Contains (t , string (respBody ), "alphanumeric" , "[list] expected alphanumeric rejection for %s" , tc .name )
755+ framework .L .Info ().Msgf ("[list] %s correctly rejected: %s" , tc .name , string (respBody ))
756+ }
757+
758+ framework .L .Info ().Msg ("All identifier validation checks passed" )
759+ }
0 commit comments