@@ -2408,6 +2408,67 @@ export function resetAgentExternallyKilled(): void {
24082408 *
24092409 * @param workDir - AWF working directory (contains docker-compose.yml)
24102410 */
2411+ function isSensitiveComposeEnvVar ( name : string ) : boolean {
2412+ return / ( T O K E N | K E Y | S E C R E T ) / i. test ( name ) ;
2413+ }
2414+
2415+ function sanitizeComposeEnvironment ( environment : unknown ) : void {
2416+ if ( Array . isArray ( environment ) ) {
2417+ for ( let i = 0 ; i < environment . length ; i ++ ) {
2418+ const entry = environment [ i ] ;
2419+ if ( typeof entry !== 'string' ) {
2420+ continue ;
2421+ }
2422+
2423+ const separatorIndex = entry . indexOf ( '=' ) ;
2424+ if ( separatorIndex === - 1 ) {
2425+ continue ;
2426+ }
2427+
2428+ const key = entry . slice ( 0 , separatorIndex ) ;
2429+ if ( isSensitiveComposeEnvVar ( key ) ) {
2430+ environment [ i ] = `${ key } =[REDACTED]` ;
2431+ }
2432+ }
2433+ return ;
2434+ }
2435+
2436+ if ( environment && typeof environment === 'object' ) {
2437+ const values = environment as Record < string , unknown > ;
2438+ for ( const key of Object . keys ( values ) ) {
2439+ if ( isSensitiveComposeEnvVar ( key ) ) {
2440+ values [ key ] = '[REDACTED]' ;
2441+ }
2442+ }
2443+ }
2444+ }
2445+
2446+ function sanitizeDockerComposeYaml ( raw : string ) : string {
2447+ const parsed = yaml . load ( raw ) ;
2448+ if ( ! parsed || typeof parsed !== 'object' ) {
2449+ return raw ;
2450+ }
2451+
2452+ const compose = parsed as Record < string , unknown > ;
2453+ const services = compose . services ;
2454+ if ( ! services || typeof services !== 'object' || Array . isArray ( services ) ) {
2455+ return yaml . dump ( compose , { lineWidth : - 1 } ) ;
2456+ }
2457+
2458+ for ( const service of Object . values ( services as Record < string , unknown > ) ) {
2459+ if ( ! service || typeof service !== 'object' || Array . isArray ( service ) ) {
2460+ continue ;
2461+ }
2462+
2463+ const serviceConfig = service as Record < string , unknown > ;
2464+ if ( 'environment' in serviceConfig ) {
2465+ sanitizeComposeEnvironment ( serviceConfig . environment ) ;
2466+ }
2467+ }
2468+
2469+ return yaml . dump ( compose , { lineWidth : - 1 } ) ;
2470+ }
2471+
24112472export async function collectDiagnosticLogs ( workDir : string ) : Promise < void > {
24122473 const diagnosticsDir = path . join ( workDir , 'diagnostics' ) ;
24132474 try {
@@ -2471,18 +2532,14 @@ export async function collectDiagnosticLogs(workDir: string): Promise<void> {
24712532 }
24722533 }
24732534
2474- // Write a sanitized copy of docker-compose.yml — redact values of env vars
2475- // whose name contains TOKEN, KEY, or SECRET (case-insensitive, e.g. github_token, API_KEY, Api_Key).
2476- // \w* matches word characters [A-Za-z0-9_] so all valid identifier characters are covered .
2535+ // Write a sanitized copy of docker-compose.yml by parsing the YAML and redacting
2536+ // sensitive environment variable values under services[*].environment in both
2537+ // object/map and list forms .
24772538 const composeFile = path . join ( workDir , 'docker-compose.yml' ) ;
24782539 if ( fs . existsSync ( composeFile ) ) {
24792540 try {
24802541 const raw = fs . readFileSync ( composeFile , 'utf8' ) ;
2481- // Match lines like: SOME_TOKEN_VAR: value or api_key: "value" or Api_Key: mixed
2482- const sanitized = raw . replace (
2483- / ^ ( \s + \w * (?: T O K E N | K E Y | S E C R E T ) \w * \s * : .* ) $ / gim,
2484- match => match . replace ( / : \s * ( .* ) $ / , ': [REDACTED]' )
2485- ) ;
2542+ const sanitized = sanitizeDockerComposeYaml ( raw ) ;
24862543 fs . writeFileSync ( path . join ( diagnosticsDir , 'docker-compose.yml' ) , sanitized ) ;
24872544 } catch ( error ) {
24882545 logger . debug ( 'Could not write sanitized docker-compose.yml to diagnostics:' , error ) ;
0 commit comments