2929namespace DataMachineCode \Channels ;
3030
3131use DataMachineCode \Environment ;
32+ use DataMachineCode \Support \SecretRedactor ;
3233use WP_Error ;
3334
3435defined ('ABSPATH ' ) || exit;
@@ -163,13 +164,16 @@ public static function execute( array $input ) {
163164 $ detach = (bool ) ( $ config ['detach ' ] ?? true );
164165 $ timeout = isset ($ config ['timeout ' ]) && is_int ($ config ['timeout ' ]) ? $ config ['timeout ' ] : self ::DEFAULT_TIMEOUT_SECONDS ;
165166 $ cwd = isset ($ config ['cwd ' ]) && is_string ($ config ['cwd ' ]) && '' !== $ config ['cwd ' ] ? $ config ['cwd ' ] : null ;
166- $ env = self ::build_env_map (isset ($ config ['env ' ]) && is_array ($ config ['env ' ]) ? $ config ['env ' ] : array ());
167+ $ env = self ::build_env_map (
168+ isset ($ config ['env ' ]) && is_array ($ config ['env ' ]) ? $ config ['env ' ] : array (),
169+ isset ($ config ['env_from ' ]) && is_array ($ config ['env_from ' ]) ? $ config ['env_from ' ] : array ()
170+ );
167171
168172 if ( $ detach ) {
169- return self ::dispatch_detached ($ channel , $ recipient , $ command_args , $ cwd , $ env );
173+ return self ::dispatch_detached ($ channel , $ recipient , $ command_args , $ cwd , $ env[ ' values ' ] );
170174 }
171175
172- return self ::dispatch_sync ($ channel , $ recipient , $ command_args , $ cwd , $ env , $ timeout );
176+ return self ::dispatch_sync ($ channel , $ recipient , $ command_args , $ cwd , $ env[ ' values ' ], $ env [ ' secrets ' ] , $ timeout );
173177 }
174178
175179 /**
@@ -232,7 +236,7 @@ private static function dispatch_detached( string $channel, string $recipient, a
232236 * @param int $timeout Timeout in seconds.
233237 * @return array<string, mixed>|WP_Error
234238 */
235- private static function dispatch_sync ( string $ channel , string $ recipient , array $ argv , ?string $ cwd , ?array $ env , int $ timeout ) {
239+ private static function dispatch_sync ( string $ channel , string $ recipient , array $ argv , ?string $ cwd , ?array $ env , array $ secrets , int $ timeout ) {
236240 $ descriptors = array (
237241 0 => array ( 'pipe ' , 'r ' ),
238242 1 => array ( 'pipe ' , 'w ' ),
@@ -328,8 +332,8 @@ private static function dispatch_sync( string $channel, string $recipient, array
328332 array (
329333 'channel ' => $ channel ,
330334 'recipient ' => $ recipient ,
331- 'stdout ' => self ::truncate_output ($ stdout ),
332- 'stderr ' => self ::truncate_output ($ stderr ),
335+ 'stdout ' => self ::truncate_output ($ stdout, $ secrets ),
336+ 'stderr ' => self ::truncate_output ($ stderr, $ secrets ),
333337 'duration_ms ' => $ duration_ms ,
334338 )
335339 );
@@ -343,8 +347,8 @@ private static function dispatch_sync( string $channel, string $recipient, array
343347 'channel ' => $ channel ,
344348 'recipient ' => $ recipient ,
345349 'exit_code ' => $ exit_code ,
346- 'stdout ' => self ::truncate_output ($ stdout ),
347- 'stderr ' => self ::truncate_output ($ stderr ),
350+ 'stdout ' => self ::truncate_output ($ stdout, $ secrets ),
351+ 'stderr ' => self ::truncate_output ($ stderr, $ secrets ),
348352 'duration_ms ' => $ duration_ms ,
349353 )
350354 );
@@ -359,8 +363,8 @@ private static function dispatch_sync( string $channel, string $recipient, array
359363 'mode ' => 'sync ' ,
360364 'exit_code ' => $ exit_code ,
361365 'duration_ms ' => $ duration_ms ,
362- 'stdout ' => self ::truncate_output ($ stdout ),
363- 'stderr ' => self ::truncate_output ($ stderr ),
366+ 'stdout ' => self ::truncate_output ($ stdout, $ secrets ),
367+ 'stderr ' => self ::truncate_output ($ stderr, $ secrets ),
364368 ),
365369 );
366370 }
@@ -415,10 +419,12 @@ private static function open_process( array $argv, array $descriptors, ?string $
415419 * the inherited PATH if it provides one.
416420 *
417421 * @param array<string, string> $configured Configured env map.
418- * @return array<string, string>
422+ * @param array<string, string> $env_from Child env name => parent env name.
423+ * @return array{values:array<string,string>,secrets:string[]}
419424 */
420- private static function build_env_map ( array $ configured ): array {
421- $ env = array ();
425+ private static function build_env_map ( array $ configured , array $ env_from ): array {
426+ $ env = array ();
427+ $ secrets = array ();
422428
423429 $ parent_path = getenv ('PATH ' );
424430 if ( is_string ($ parent_path ) && '' !== $ parent_path ) {
@@ -427,9 +433,42 @@ private static function build_env_map( array $configured ): array {
427433
428434 foreach ( $ configured as $ key => $ value ) {
429435 $ env [ $ key ] = $ value ;
436+ if ( self ::is_secret_like_env_key ( (string ) $ key ) ) {
437+ $ secrets [] = $ value ;
438+ }
430439 }
431440
432- return $ env ;
441+ foreach ( $ env_from as $ target_key => $ source_key ) {
442+ $ value = self ::parent_env ( (string ) $ source_key );
443+ if ( '' === $ value ) {
444+ continue ;
445+ }
446+
447+ $ env [ $ target_key ] = $ value ;
448+ if ( self ::is_secret_like_env_key ( (string ) $ target_key ) || self ::is_secret_like_env_key ( (string ) $ source_key ) ) {
449+ $ secrets [] = $ value ;
450+ }
451+ }
452+
453+ return array (
454+ 'values ' => $ env ,
455+ 'secrets ' => array_values (array_unique (array_filter ($ secrets , static fn ( string $ secret ): bool => strlen (trim ($ secret )) >= 8 ))),
456+ );
457+ }
458+
459+ /**
460+ * Read a trimmed parent environment variable.
461+ */
462+ private static function parent_env ( string $ name ): string {
463+ $ value = getenv ($ name );
464+ return is_string ($ value ) ? trim ($ value ) : '' ;
465+ }
466+
467+ /**
468+ * Determine whether an environment key conventionally carries a secret.
469+ */
470+ private static function is_secret_like_env_key ( string $ key ): bool {
471+ return 1 === preg_match ('/(?:^|_)(TOKEN|SECRET|PASSWORD|PASSWD|PRIVATE_KEY|API_KEY|ACCESS_KEY|AUTH|COOKIE|NONCE)(?:_|$)/i ' , $ key );
433472 }
434473
435474 /**
@@ -438,8 +477,9 @@ private static function build_env_map( array $configured ): array {
438477 * @param string $output Captured output.
439478 * @return string Truncated output.
440479 */
441- private static function truncate_output ( string $ output ): string {
442- $ limit = 8192 ;
480+ private static function truncate_output ( string $ output , array $ secrets = array () ): string {
481+ $ output = SecretRedactor::redact ($ output , $ secrets );
482+ $ limit = 8192 ;
443483 if ( strlen ($ output ) <= $ limit ) {
444484 return $ output ;
445485 }
0 commit comments