@@ -324,8 +324,8 @@ describe("resolveAllowAlwaysPatterns", () => {
324324 const patterns = resolveAllowAlwaysPatterns ( {
325325 segments : [
326326 {
327- raw : "/bin/zsh -lc 'whoami'" ,
328- argv : [ "/bin/zsh" , "-lc " , "whoami" ] ,
327+ raw : "/bin/zsh -c 'whoami'" ,
328+ argv : [ "/bin/zsh" , "-c " , "whoami" ] ,
329329 resolution : makeMockCommandResolution ( {
330330 execution : makeMockExecutableResolution ( {
331331 rawExecutable : "/bin/zsh" ,
@@ -353,8 +353,8 @@ describe("resolveAllowAlwaysPatterns", () => {
353353 const patterns = resolveAllowAlwaysPatterns ( {
354354 segments : [
355355 {
356- raw : "/bin/zsh -lc 'whoami && ls && whoami'" ,
357- argv : [ "/bin/zsh" , "-lc " , "whoami && ls && whoami" ] ,
356+ raw : "/bin/zsh -c 'whoami && ls && whoami'" ,
357+ argv : [ "/bin/zsh" , "-c " , "whoami && ls && whoami" ] ,
358358 resolution : makeMockCommandResolution ( {
359359 execution : makeMockExecutableResolution ( {
360360 rawExecutable : "/bin/zsh" ,
@@ -437,12 +437,49 @@ describe("resolveAllowAlwaysPatterns", () => {
437437 }
438438 } ) ;
439439
440+ it ( "rejects startup shell inline payloads for allow-always and inline-chain allowlist fallback" , ( ) => {
441+ if ( process . platform === "win32" ) {
442+ return ;
443+ }
444+ const dir = makeTempDir ( ) ;
445+ const tool = makeExecutable ( dir , "openclaw-ok" ) ;
446+ const env = { PATH : `${ dir } ${ path . delimiter } ${ process . env . PATH ?? "" } ` } ;
447+ const safeBins = resolveSafeBins ( undefined ) ;
448+
449+ for ( const command of [
450+ `bash --login -c "openclaw-ok && openclaw-ok"` ,
451+ `bash -i -c "openclaw-ok && openclaw-ok"` ,
452+ `bash -lc "openclaw-ok && openclaw-ok"` ,
453+ `bash --login -c '$0 "$1"' ${ tool } marker` ,
454+ `bash -i -c '$0 "$1"' ${ tool } marker` ,
455+ `bash -lc '$0 "$1"' ${ tool } marker` ,
456+ ] ) {
457+ const { persisted } = resolvePersistedPatterns ( {
458+ command,
459+ dir,
460+ env,
461+ safeBins,
462+ } ) ;
463+ expect ( persisted ) . toEqual ( [ ] ) ;
464+
465+ const second = evaluateShellAllowlist ( {
466+ command,
467+ allowlist : [ { pattern : tool } ] ,
468+ safeBins,
469+ cwd : dir ,
470+ env,
471+ platform : process . platform ,
472+ } ) ;
473+ expect ( second . allowlistSatisfied ) . toBe ( false ) ;
474+ }
475+ } ) ;
476+
440477 it ( "rejects shell-wrapper positional argv carriers" , ( ) => {
441478 if ( process . platform === "win32" ) {
442479 return ;
443480 }
444481 expectPositionalArgvCarrierResult ( {
445- command : `sh -lc '$0 "$1"' touch {marker}` ,
482+ command : `sh -c '$0 "$1"' touch {marker}` ,
446483 expectPersisted : true ,
447484 } ) ;
448485 } ) ;
@@ -452,7 +489,7 @@ describe("resolveAllowAlwaysPatterns", () => {
452489 return ;
453490 }
454491 expectPositionalArgvCarrierResult ( {
455- command : `sh -lc 'exec -- "$0" "$1"' touch {marker}` ,
492+ command : `sh -c 'exec -- "$0" "$1"' touch {marker}` ,
456493 expectPersisted : true ,
457494 } ) ;
458495 } ) ;
@@ -462,7 +499,7 @@ describe("resolveAllowAlwaysPatterns", () => {
462499 return ;
463500 }
464501 expectPositionalArgvCarrierResult ( {
465- command : `sh -lc "'$0' "$1"" touch {marker}` ,
502+ command : `sh -c "'$0' "$1"" touch {marker}` ,
466503 expectPersisted : false ,
467504 } ) ;
468505 } ) ;
@@ -472,7 +509,7 @@ describe("resolveAllowAlwaysPatterns", () => {
472509 return ;
473510 }
474511 expectPositionalArgvCarrierResult ( {
475- command : `sh -lc "exec
512+ command : `sh -c "exec
476513$0 \\"$1\\"" touch {marker}` ,
477514 expectPersisted : false ,
478515 } ) ;
@@ -489,15 +526,15 @@ $0 \\"$1\\"" touch {marker}`,
489526 const marker = path . join ( dir , "marker" ) ;
490527
491528 const { persisted } = resolvePersistedPatterns ( {
492- command : `sh -lc 'echo blocked; $0 "$1"' touch ${ marker } ` ,
529+ command : `sh -c 'echo blocked; $0 "$1"' touch ${ marker } ` ,
493530 dir,
494531 env,
495532 safeBins,
496533 } ) ;
497534 expect ( persisted ) . not . toContain ( touch ) ;
498535
499536 const second = evaluateShellAllowlist ( {
500- command : `sh -lc 'echo blocked; $0 "$1"' touch ${ marker } ` ,
537+ command : `sh -c 'echo blocked; $0 "$1"' touch ${ marker } ` ,
501538 allowlist : [ { pattern : touch } ] ,
502539 safeBins,
503540 cwd : dir ,
@@ -515,7 +552,7 @@ $0 \\"$1\\"" touch {marker}`,
515552 expectAllowAlwaysBypassBlocked ( {
516553 dir,
517554 firstCommand : "bash scripts/save_crystal.sh" ,
518- secondCommand : "bash -lc 'scripts/save_crystal.sh'" ,
555+ secondCommand : "bash -c 'scripts/save_crystal.sh'" ,
519556 env,
520557 persistedPattern : script ,
521558 } ) ;
@@ -564,8 +601,8 @@ $0 \\"$1\\"" touch {marker}`,
564601 const patterns = resolveAllowAlwaysPatterns ( {
565602 segments : [
566603 {
567- raw : "/usr/local/bin/zsh -lc whoami" ,
568- argv : [ "/usr/local/bin/zsh" , "-lc " , "whoami" ] ,
604+ raw : "/usr/local/bin/zsh -c whoami" ,
605+ argv : [ "/usr/local/bin/zsh" , "-c " , "whoami" ] ,
569606 resolution : makeMockCommandResolution ( {
570607 execution : makeMockExecutableResolution ( {
571608 rawExecutable : "/usr/local/bin/zsh" ,
@@ -591,8 +628,8 @@ $0 \\"$1\\"" touch {marker}`,
591628 const patterns = resolveAllowAlwaysPatterns ( {
592629 segments : [
593630 {
594- raw : "/usr/bin/nice /bin/zsh -lc whoami" ,
595- argv : [ "/usr/bin/nice" , "/bin/zsh" , "-lc " , "whoami" ] ,
631+ raw : "/usr/bin/nice /bin/zsh -c whoami" ,
632+ argv : [ "/usr/bin/nice" , "/bin/zsh" , "-c " , "whoami" ] ,
596633 resolution : makeMockCommandResolution ( {
597634 execution : makeMockExecutableResolution ( {
598635 rawExecutable : "/usr/bin/nice" ,
@@ -619,8 +656,8 @@ $0 \\"$1\\"" touch {marker}`,
619656 const patterns = resolveAllowAlwaysPatterns ( {
620657 segments : [
621658 {
622- raw : "/usr/bin/time -p /bin/zsh -lc whoami" ,
623- argv : [ "/usr/bin/time" , "-p" , "/bin/zsh" , "-lc " , "whoami" ] ,
659+ raw : "/usr/bin/time -p /bin/zsh -c whoami" ,
660+ argv : [ "/usr/bin/time" , "-p" , "/bin/zsh" , "-c " , "whoami" ] ,
624661 resolution : makeMockCommandResolution ( {
625662 execution : makeMockExecutableResolution ( {
626663 rawExecutable : "/usr/bin/time" ,
@@ -650,8 +687,8 @@ $0 \\"$1\\"" touch {marker}`,
650687 const patterns = resolveAllowAlwaysPatterns ( {
651688 segments : [
652689 {
653- raw : `${ busybox } sh -lc whoami` ,
654- argv : [ busybox , "sh" , "-lc " , "whoami" ] ,
690+ raw : `${ busybox } sh -c whoami` ,
691+ argv : [ busybox , "sh" , "-c " , "whoami" ] ,
655692 resolution : makeMockCommandResolution ( {
656693 execution : makeMockExecutableResolution ( {
657694 rawExecutable : busybox ,
@@ -744,8 +781,8 @@ $0 \\"$1\\"" touch {marker}`,
744781 const env = makePathEnv ( dir ) ;
745782 expectAllowAlwaysBypassBlocked ( {
746783 dir,
747- firstCommand : "/usr/bin/caffeinate -d -w 42 /bin/zsh -lc 'echo warmup-ok'" ,
748- secondCommand : "/usr/bin/caffeinate -d -w 42 /bin/zsh -lc 'id > marker'" ,
784+ firstCommand : "/usr/bin/caffeinate -d -w 42 /bin/zsh -c 'echo warmup-ok'" ,
785+ secondCommand : "/usr/bin/caffeinate -d -w 42 /bin/zsh -c 'id > marker'" ,
749786 env,
750787 persistedPattern : echo ,
751788 } ) ;
@@ -761,8 +798,8 @@ $0 \\"$1\\"" touch {marker}`,
761798 const env = makePathEnv ( dir ) ;
762799 expectAllowAlwaysBypassBlocked ( {
763800 dir,
764- firstCommand : "/usr/bin/nice /bin/zsh -lc 'echo warmup-ok'" ,
765- secondCommand : "/usr/bin/nice /bin/zsh -lc 'id > marker'" ,
801+ firstCommand : "/usr/bin/nice /bin/zsh -c 'echo warmup-ok'" ,
802+ secondCommand : "/usr/bin/nice /bin/zsh -c 'id > marker'" ,
766803 env,
767804 persistedPattern : echo ,
768805 } ) ;
@@ -779,8 +816,8 @@ $0 \\"$1\\"" touch {marker}`,
779816 expectAllowAlwaysBypassBlocked ( {
780817 dir,
781818 firstCommand :
782- "/usr/bin/sandbox-exec -p '(deny default) (allow process*)' /bin/zsh -lc 'echo warmup-ok'" ,
783- secondCommand : "/usr/bin/sandbox-exec -p '(allow default)' /bin/zsh -lc 'id > marker'" ,
819+ "/usr/bin/sandbox-exec -p '(deny default) (allow process*)' /bin/zsh -c 'echo warmup-ok'" ,
820+ secondCommand : "/usr/bin/sandbox-exec -p '(allow default)' /bin/zsh -c 'id > marker'" ,
784821 env,
785822 persistedPattern : echo ,
786823 } ) ;
@@ -796,8 +833,8 @@ $0 \\"$1\\"" touch {marker}`,
796833 const env = makePathEnv ( dir ) ;
797834 expectAllowAlwaysBypassBlocked ( {
798835 dir,
799- firstCommand : "/usr/bin/time -p /bin/zsh -lc 'echo warmup-ok'" ,
800- secondCommand : "/usr/bin/time -p /bin/zsh -lc 'id > marker'" ,
836+ firstCommand : "/usr/bin/time -p /bin/zsh -c 'echo warmup-ok'" ,
837+ secondCommand : "/usr/bin/time -p /bin/zsh -c 'id > marker'" ,
801838 env,
802839 persistedPattern : echo ,
803840 } ) ;
@@ -813,15 +850,15 @@ $0 \\"$1\\"" touch {marker}`,
813850 const env = makePathEnv ( dir ) ;
814851 expectAllowAlwaysBypassBlocked ( {
815852 dir,
816- firstCommand : "/usr/bin/arch -arm64 /bin/zsh -lc 'echo warmup-ok'" ,
817- secondCommand : "/usr/bin/arch -arm64 /bin/zsh -lc 'id > marker-arch'" ,
853+ firstCommand : "/usr/bin/arch -arm64 /bin/zsh -c 'echo warmup-ok'" ,
854+ secondCommand : "/usr/bin/arch -arm64 /bin/zsh -c 'id > marker-arch'" ,
818855 env,
819856 persistedPattern : echo ,
820857 } ) ;
821858 expectAllowAlwaysBypassBlocked ( {
822859 dir,
823- firstCommand : "/usr/bin/xcrun /bin/zsh -lc 'echo warmup-ok'" ,
824- secondCommand : "/usr/bin/xcrun /bin/zsh -lc 'id > marker-xcrun'" ,
860+ firstCommand : "/usr/bin/xcrun /bin/zsh -c 'echo warmup-ok'" ,
861+ secondCommand : "/usr/bin/xcrun /bin/zsh -c 'id > marker-xcrun'" ,
825862 env,
826863 persistedPattern : echo ,
827864 } ) ;
@@ -873,15 +910,15 @@ $0 \\"$1\\"" touch {marker}`,
873910 const safeBins = resolveSafeBins ( undefined ) ;
874911
875912 const { persisted } = resolvePersistedPatterns ( {
876- command : `sh -lc '$0 "$@"' awk '{print $1}' data.csv` ,
913+ command : `sh -c '$0 "$@"' awk '{print $1}' data.csv` ,
877914 dir,
878915 env,
879916 safeBins,
880917 } ) ;
881918 expect ( persisted ) . toEqual ( [ ] ) ;
882919
883920 const second = evaluateShellAllowlist ( {
884- command : `sh -lc '$0 "$@"' awk 'BEGIN{system("id > /tmp/pwned")}'` ,
921+ command : `sh -c '$0 "$@"' awk 'BEGIN{system("id > /tmp/pwned")}'` ,
885922 allowlist : persisted . map ( ( pattern ) => ( { pattern } ) ) ,
886923 safeBins,
887924 cwd : dir ,
@@ -901,8 +938,8 @@ $0 \\"$1\\"" touch {marker}`,
901938 const env = makePathEnv ( dir ) ;
902939 expectAllowAlwaysBypassBlocked ( {
903940 dir,
904- firstCommand : "/usr/bin/script -q /dev/null /bin/sh -lc 'echo warmup-ok'" ,
905- secondCommand : "/usr/bin/script -q /dev/null /bin/sh -lc 'id > marker'" ,
941+ firstCommand : "/usr/bin/script -q /dev/null /bin/sh -c 'echo warmup-ok'" ,
942+ secondCommand : "/usr/bin/script -q /dev/null /bin/sh -c 'id > marker'" ,
906943 env,
907944 persistedPattern : echo ,
908945 } ) ;
@@ -935,15 +972,15 @@ $0 \\"$1\\"" touch {marker}`,
935972 const safeBins = resolveSafeBins ( undefined ) ;
936973
937974 const { persisted } = resolvePersistedPatterns ( {
938- command : `sh -lc '$0 "$@"' env echo SAFE` ,
975+ command : `sh -c '$0 "$@"' env echo SAFE` ,
939976 dir,
940977 env,
941978 safeBins,
942979 } ) ;
943980 expect ( persisted ) . toEqual ( [ ] ) ;
944981
945982 const second = evaluateShellAllowlist ( {
946- command : `sh -lc '$0 "$@"' env BASH_ENV=/tmp/payload.sh bash -lc 'id > /tmp/pwned'` ,
983+ command : `sh -c '$0 "$@"' env BASH_ENV=/tmp/payload.sh bash -c 'id > /tmp/pwned'` ,
947984 allowlist : [ { pattern : envPath } ] ,
948985 safeBins,
949986 cwd : dir ,
@@ -963,15 +1000,15 @@ $0 \\"$1\\"" touch {marker}`,
9631000 const safeBins = resolveSafeBins ( undefined ) ;
9641001
9651002 const { persisted } = resolvePersistedPatterns ( {
966- command : `sh -lc '$0 "$@"' bash -lc 'echo safe'` ,
1003+ command : `sh -c '$0 "$@"' bash -c 'echo safe'` ,
9671004 dir,
9681005 env,
9691006 safeBins,
9701007 } ) ;
9711008 expect ( persisted ) . toEqual ( [ ] ) ;
9721009
9731010 const second = evaluateShellAllowlist ( {
974- command : `sh -lc '$0 "$@"' bash -lc 'id > /tmp/pwned'` ,
1011+ command : `sh -c '$0 "$@"' bash -c 'id > /tmp/pwned'` ,
9751012 allowlist : [ { pattern : bashPath } ] ,
9761013 safeBins,
9771014 cwd : dir ,
@@ -991,15 +1028,15 @@ $0 \\"$1\\"" touch {marker}`,
9911028 const safeBins = resolveSafeBins ( undefined ) ;
9921029
9931030 const { persisted } = resolvePersistedPatterns ( {
994- command : `sh -lc '$0 "$@"' xargs echo SAFE` ,
1031+ command : `sh -c '$0 "$@"' xargs echo SAFE` ,
9951032 dir,
9961033 env,
9971034 safeBins,
9981035 } ) ;
9991036 expect ( persisted ) . toEqual ( [ ] ) ;
10001037
10011038 const second = evaluateShellAllowlist ( {
1002- command : `sh -lc '$0 "$@"' xargs sh -lc 'id > /tmp/pwned'` ,
1039+ command : `sh -c '$0 "$@"' xargs sh -c 'id > /tmp/pwned'` ,
10031040 allowlist : [ { pattern : xargsPath } ] ,
10041041 safeBins,
10051042 cwd : dir ,
0 commit comments