@@ -11,36 +11,42 @@ import { writeFile, readFile } from 'fs/promises';
1111import { REPO_ROOT } from '@kbn/repo-info' ;
1212import { schema } from '@kbn/config-schema' ;
1313
14- const SECURITY_GEN_AI_CONNECTORS_ENV_VAR = 'KIBANA_SECURITY_TESTING_AI_CONNECTORS' ;
15- const SECURITY_GEN_AI_LANGSMITH_KEY_ENV_VAR = 'KIBANA_SECURITY_TESTING_LANGSMITH_KEY' ;
16-
17- // siem-team secrets discussed w/ operations and we will mirror them here
18- // const SECURITY_GEN_AI_VAULT = 'secret/siem-team/security-gen-ai';
19-
20- // CI Vault
21- const SECURITY_GEN_AI_VAULT = 'secret/ci/elastic-kibana/security-gen-ai' ;
22- const SECURITY_GEN_AI_VAULT_CONNECTORS = `${ SECURITY_GEN_AI_VAULT } /connectors` ;
23- const SECURITY_GEN_AI_VAULT_LANGSMITH = `${ SECURITY_GEN_AI_VAULT } /langsmith` ;
24- const SECURITY_GEN_AI_CONNECTORS_FIELD = 'config' ;
25- const SECURITY_GEN_AI_LANGSMITH_FIELD = 'key' ;
26- const CONNECTOR_FILE = Path . join (
27- REPO_ROOT ,
28- 'x-pack/test/security_solution_api_integration/scripts/genai/vault/connector_config.json'
29- ) ;
30- const LANGSMITH_FILE = Path . join (
14+ // Environment variable set within BuildKite and read from in FTR tests
15+ // CI env vars are set by .buildkite/scripts/common/setup_job_env.sh
16+ const KIBANA_SECURITY_GEN_AI_CONFIG = 'KIBANA_SECURITY_GEN_AI_CONFIG' ;
17+
18+ // Vault paths
19+ // siem-team users (secrets.elastic.co vault) do not have access to the ci-prod vault, so secrets
20+ // are mirrored between the two vaults
21+ type VaultType = 'siem-team' | 'ci-prod' ;
22+ const VAULT_PATHS : Record < VaultType , string > = {
23+ 'siem-team' : 'secret/siem-team/security-gen-ai' ,
24+ 'ci-prod' : 'secret/ci/elastic-kibana/security-gen-ai' ,
25+ } ;
26+
27+ const getVaultPath = ( vault : VaultType = 'siem-team' ) => {
28+ return VAULT_PATHS [ vault ] ;
29+ } ;
30+
31+ const SECURITY_GEN_AI_CONFIG_FIELD = 'config' ;
32+ const SECURITY_GEN_AI_CONFIG_FILE = Path . join (
3133 REPO_ROOT ,
32- 'x-pack/test/security_solution_api_integration/scripts/genai/vault/langsmith_key.txt '
34+ 'x-pack/test/security_solution_api_integration/scripts/genai/vault/config.json '
3335) ;
3436
35- const connectorsSchema = schema . recordOf (
36- schema . string ( ) ,
37- schema . object ( {
38- name : schema . string ( ) ,
39- actionTypeId : schema . string ( ) ,
40- config : schema . recordOf ( schema . string ( ) , schema . any ( ) ) ,
41- secrets : schema . recordOf ( schema . string ( ) , schema . any ( ) ) ,
42- } )
43- ) ;
37+ const configSchema = schema . object ( {
38+ evaluatorConnectorId : schema . string ( ) ,
39+ langsmithKey : schema . string ( ) ,
40+ connectors : schema . recordOf (
41+ schema . string ( ) ,
42+ schema . object ( {
43+ name : schema . string ( ) ,
44+ actionTypeId : schema . string ( ) ,
45+ config : schema . recordOf ( schema . string ( ) , schema . any ( ) ) ,
46+ secrets : schema . recordOf ( schema . string ( ) , schema . any ( ) ) ,
47+ } )
48+ ) ,
49+ } ) ;
4450
4551export interface AvailableConnector {
4652 name : string ;
@@ -49,50 +55,75 @@ export interface AvailableConnector {
4955 secrets : Record < string , unknown > ;
5056}
5157
52- export const retrieveFromVault = async (
53- vault : string ,
54- filePath : string ,
55- field : string ,
56- isJson = true
57- ) => {
58+ /**
59+ * Retrieve generic value from vault and write to file
60+ *
61+ * @param vault
62+ * @param filePath
63+ * @param field
64+ */
65+ export const retrieveFromVault = async ( vault : string , filePath : string , field : string ) => {
5866 const { stdout } = await execa ( 'vault' , [ 'read' , `-field=${ field } ` , vault ] , {
5967 cwd : REPO_ROOT ,
6068 buffer : true ,
6169 } ) ;
6270
6371 const value = Buffer . from ( stdout , 'base64' ) . toString ( 'utf-8' ) . trim ( ) ;
64- const config = isJson ? JSON . stringify ( JSON . parse ( value ) , null , 2 ) : value ;
72+ const config = JSON . stringify ( JSON . parse ( value ) , null , 2 ) ;
6573
6674 await writeFile ( filePath , config ) ;
6775
6876 // eslint-disable-next-line no-console
69- console . log ( `Config dumped into ${ filePath } ` ) ;
77+ console . log ( `Config written to: ${ filePath } ` ) ;
7078} ;
7179
72- export const retrieveConnectorConfig = async ( ) => {
73- await retrieveFromVault (
74- SECURITY_GEN_AI_VAULT_CONNECTORS ,
75- CONNECTOR_FILE ,
76- SECURITY_GEN_AI_CONNECTORS_FIELD
77- ) ;
78- } ;
79-
80- export const retrieveLangsmithKey = async ( ) => {
80+ /**
81+ * Retrieve Security Gen AI secrets config from vault and write to file
82+ * @param vault
83+ */
84+ export const retrieveConfigFromVault = async ( vault : VaultType = 'siem-team' ) => {
8185 await retrieveFromVault (
82- SECURITY_GEN_AI_VAULT_LANGSMITH ,
83- LANGSMITH_FILE ,
84- SECURITY_GEN_AI_LANGSMITH_FIELD ,
85- false
86+ getVaultPath ( vault ) ,
87+ SECURITY_GEN_AI_CONFIG_FILE ,
88+ SECURITY_GEN_AI_CONFIG_FIELD
8689 ) ;
8790} ;
8891
89- export const formatCurrentConfig = async ( filePath : string ) => {
90- const config = await readFile ( filePath , 'utf-8' ) ;
92+ /**
93+ * Returns command for manually working with secrets from `config.json`.
94+ * Format can be either 'vault-write' (for vault command) or 'env-var' (for environment variable).
95+ * Run this command and share with @kibana-ops via https://p.elstc.co to make updating secrets easier, or for pasting
96+ * custom configs into the BuildKite pipeline: https://buildkite.com/elastic/kibana-ess-security-solution-gen-ai-evals
97+
98+ * Alternatively, have @kibana-ops run the following to update the secrets for CI:
99+ *
100+ * node retrieve_secrets.js --vault siem-team
101+ * node upload_secrets.js --vault ci-prod
102+ *
103+ * @param format - The format of the command to return ('vault-write' or 'env-var')
104+ * @param vault - The vault to use (only applicable for 'vault-write' format)
105+ */
106+ export const getCommand = async (
107+ format : 'vault-write' | 'env-var' = 'vault-write' ,
108+ vault : VaultType = 'ci-prod'
109+ ) => {
110+ const config = await readFile ( SECURITY_GEN_AI_CONFIG_FILE , 'utf-8' ) ;
91111 const asB64 = Buffer . from ( config ) . toString ( 'base64' ) ;
92- // eslint-disable-next-line no-console
93- console . log ( asB64 ) ;
112+
113+ if ( format === 'vault-write' ) {
114+ return `vault write ${ getVaultPath ( vault ) } ${ SECURITY_GEN_AI_CONFIG_FIELD } =${ asB64 } ` ;
115+ } else {
116+ return `${ KIBANA_SECURITY_GEN_AI_CONFIG } =${ asB64 } ` ;
117+ }
94118} ;
95119
120+ /**
121+ * Write generic value to vault from a file
122+ *
123+ * @param vault
124+ * @param filePath
125+ * @param field
126+ */
96127export const uploadToVault = async ( vault : string , filePath : string , field : string ) => {
97128 const config = await readFile ( filePath , 'utf-8' ) ;
98129 const asB64 = Buffer . from ( config ) . toString ( 'base64' ) ;
@@ -103,69 +134,35 @@ export const uploadToVault = async (vault: string, filePath: string, field: stri
103134 } ) ;
104135} ;
105136
106- export const uploadConnectorConfigToVault = async ( ) => {
107- await uploadToVault (
108- SECURITY_GEN_AI_VAULT_CONNECTORS ,
109- CONNECTOR_FILE ,
110- SECURITY_GEN_AI_CONNECTORS_FIELD
111- ) ;
112- } ;
113-
114- export const uploadLangsmithKeyToVault = async ( ) => {
137+ /**
138+ * Read Security Gen AI secrets from `config.json` and upload to vault
139+ * @param vault
140+ */
141+ export const uploadConfigToVault = async ( vault : VaultType = 'siem-team' ) => {
115142 await uploadToVault (
116- SECURITY_GEN_AI_VAULT_LANGSMITH ,
117- LANGSMITH_FILE ,
118- SECURITY_GEN_AI_LANGSMITH_FIELD
143+ getVaultPath ( vault ) ,
144+ SECURITY_GEN_AI_CONFIG_FILE ,
145+ SECURITY_GEN_AI_CONFIG_FIELD
119146 ) ;
120147} ;
121148
122149/**
123- * FOR LOCAL USE ONLY! Export connectors and langsmith secrets from vault to env vars before manually
124- * running evaluations. CI env vars are set by .buildkite/scripts/common/setup_job_env.sh
150+ * Returns parsed config from environment variable
125151 */
126- export const exportToEnvVars = async ( ) => {
127- const { stdout : connectors } = await execa (
128- 'vault' ,
129- [ 'read' , `-field=${ SECURITY_GEN_AI_CONNECTORS_FIELD } ` , SECURITY_GEN_AI_VAULT_CONNECTORS ] ,
130- {
131- cwd : REPO_ROOT ,
132- buffer : true ,
133- }
134- ) ;
135- const { stdout : langsmithKey } = await execa (
136- 'vault' ,
137- [ 'read' , `-field=${ SECURITY_GEN_AI_LANGSMITH_FIELD } ` , SECURITY_GEN_AI_VAULT_LANGSMITH ] ,
138- {
139- cwd : REPO_ROOT ,
140- buffer : true ,
141- }
142- ) ;
143- process . env [ SECURITY_GEN_AI_CONNECTORS_ENV_VAR ] = connectors ;
144- process . env [ SECURITY_GEN_AI_LANGSMITH_KEY_ENV_VAR ] = langsmithKey ;
145- } ;
146-
147- export const loadConnectorsFromEnvVar = ( ) : Record < string , AvailableConnector > => {
148- const connectorsValue = process . env [ SECURITY_GEN_AI_CONNECTORS_ENV_VAR ] ;
149- if ( ! connectorsValue ) {
150- return { } ;
152+ export const getSecurityGenAIConfigFromEnvVar = ( ) => {
153+ const configValue = process . env [ KIBANA_SECURITY_GEN_AI_CONFIG ] ;
154+ if ( ! configValue ) {
155+ throw new Error ( `Environment variable ${ KIBANA_SECURITY_GEN_AI_CONFIG } does not exist!` ) ;
151156 }
152157
153- let connectors : Record < string , AvailableConnector > ;
158+ let config : typeof configSchema ;
154159 try {
155- connectors = JSON . parse ( Buffer . from ( connectorsValue , 'base64' ) . toString ( 'utf-8' ) ) ;
160+ config = JSON . parse ( Buffer . from ( configValue , 'base64' ) . toString ( 'utf-8' ) ) ;
156161 } catch ( e ) {
157162 throw new Error (
158- `Error trying to parse value from ${ SECURITY_GEN_AI_CONNECTORS_ENV_VAR } environment variable: ${ e . message } `
163+ `Error trying to parse value from ${ KIBANA_SECURITY_GEN_AI_CONFIG } environment variable: ${ e . message } `
159164 ) ;
160165 }
161- return connectorsSchema . validate ( connectors ) ;
162- } ;
163-
164- export const loadLangSmithKeyFromEnvVar = ( ) : string | undefined => {
165- const langsmithKeyValue = process . env [ SECURITY_GEN_AI_LANGSMITH_KEY_ENV_VAR ] ;
166- if ( ! langsmithKeyValue ) {
167- return undefined ;
168- }
169166
170- return Buffer . from ( langsmithKeyValue , 'base64' ) . toString ( 'utf-8' ) . trim ( ) ;
167+ return configSchema . validate ( config ) ;
171168} ;
0 commit comments