diff --git a/apps/desktop/assets/codex-bridge/.gitignore b/apps/desktop/assets/codex-bridge/.gitignore index 701f1a8..07bbbc0 100644 --- a/apps/desktop/assets/codex-bridge/.gitignore +++ b/apps/desktop/assets/codex-bridge/.gitignore @@ -1,2 +1,2 @@ # Generated by beforePack from NativeWatchBootstrap.cs; never commit opaque output. -/native-watch.exe +/native-watch.exe diff --git a/apps/desktop/assets/codex-bridge/NativeWatchBootstrap.cs b/apps/desktop/assets/codex-bridge/NativeWatchBootstrap.cs index 213ade0..cfcecea 100644 --- a/apps/desktop/assets/codex-bridge/NativeWatchBootstrap.cs +++ b/apps/desktop/assets/codex-bridge/NativeWatchBootstrap.cs @@ -1,14 +1,14 @@ -// Thin Windows CLI_PATH executable. Runtime and original are owner-provisioned, -// absolute, SHA-pinned; never consult PATH or CODEX_CLI_PATH. -using System; -using System.IO; -using System.Text; -using System.Diagnostics; -using System.Security.Cryptography; -using System.Collections.Generic; +// Thin Windows CLI_PATH executable. Runtime and original are owner-provisioned, +// absolute, SHA-pinned; never consult PATH or CODEX_CLI_PATH. +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Security.Cryptography; +using System.Collections.Generic; using System.Threading; using System.Runtime.InteropServices; -using System.Web.Script.Serialization; +using System.Web.Script.Serialization; class NativeWatchBootstrap { // Assign this bootstrap BEFORE creating Python. Descendants inherit job // membership atomically; the non-inheritable handle belongs only to us. @@ -44,46 +44,46 @@ class NativeWatchBootstrap { // Do not close here or in finally: the bootstrap is itself a member. // The OS closes this handle at exit, preserving our native exit code. } - static string Quote(string s) { - var b=new StringBuilder("\""); int slashes=0; - foreach(char c in s) { - if(c=='\\') {slashes++; continue;} - if(c=='"') { b.Append('\\',slashes*2+1); b.Append(c); } - else {b.Append('\\',slashes); b.Append(c);} - slashes=0; - } - b.Append('\\',slashes*2); b.Append('"'); return b.ToString(); - } - static string Pin(Dictionary c,string field) { - string p=(string)c[field], expected=(string)c[field+"_sha256"]; - if(!Path.IsPathRooted(p) || String.Equals(Path.GetFullPath(p),Process.GetCurrentProcess().MainModule.FileName,StringComparison.OrdinalIgnoreCase)) throw new Exception(); - using(var f=File.OpenRead(p)) using(var sha=SHA256.Create()) { - string actual=BitConverter.ToString(sha.ComputeHash(f)).Replace("-","").ToLowerInvariant(); - if(actual!=expected.ToLowerInvariant()) throw new Exception(); - } - return p; - } - static void Pump(Stream source, Stream target) { - byte[] buffer=new byte[8192]; int count; - while((count=source.Read(buffer,0,buffer.Length))>0) { target.Write(buffer,0,count); target.Flush(); } - } - static int Main(string[] args) { - try { - string manifest=Path.ChangeExtension(Process.GetCurrentProcess().MainModule.FileName,".json"); - var c=new JavaScriptSerializer().Deserialize>(File.ReadAllText(manifest)); - string original=Pin(c,"original"); - string python=Pin(c,"python"); + static string Quote(string s) { + var b=new StringBuilder("\""); int slashes=0; + foreach(char c in s) { + if(c=='\\') {slashes++; continue;} + if(c=='"') { b.Append('\\',slashes*2+1); b.Append(c); } + else {b.Append('\\',slashes); b.Append(c);} + slashes=0; + } + b.Append('\\',slashes*2); b.Append('"'); return b.ToString(); + } + static string Pin(Dictionary c,string field) { + string p=(string)c[field], expected=(string)c[field+"_sha256"]; + if(!Path.IsPathRooted(p) || String.Equals(Path.GetFullPath(p),Process.GetCurrentProcess().MainModule.FileName,StringComparison.OrdinalIgnoreCase)) throw new Exception(); + using(var f=File.OpenRead(p)) using(var sha=SHA256.Create()) { + string actual=BitConverter.ToString(sha.ComputeHash(f)).Replace("-","").ToLowerInvariant(); + if(actual!=expected.ToLowerInvariant()) throw new Exception(); + } + return p; + } + static void Pump(Stream source, Stream target) { + byte[] buffer=new byte[8192]; int count; + while((count=source.Read(buffer,0,buffer.Length))>0) { target.Write(buffer,0,count); target.Flush(); } + } + static int Main(string[] args) { + try { + string manifest=Path.ChangeExtension(Process.GetCurrentProcess().MainModule.FileName,".json"); + var c=new JavaScriptSerializer().Deserialize>(File.ReadAllText(manifest)); + string original=Pin(c,"original"); + string python=Pin(c,"python"); string script=Pin(c,"script"); OwnProcessTree(); - var argv=new List{"-u",script,manifest}; argv.AddRange(args); - var quoted=new List(); foreach(var a in argv) quoted.Add(Quote(a)); - var p=Process.Start(new ProcessStartInfo(python,String.Join(" ",quoted.ToArray())) { UseShellExecute=false, CreateNoWindow=true, RedirectStandardInput=true, RedirectStandardOutput=true, RedirectStandardError=true }); - var input=new Thread(()=> { try { Pump(Console.OpenStandardInput(),p.StandardInput.BaseStream); p.StandardInput.Close(); } catch(IOException) {} }); - input.IsBackground=true; input.Start(); - var output=new Thread(()=>Pump(p.StandardOutput.BaseStream,Console.OpenStandardOutput())); - var error=new Thread(()=>Pump(p.StandardError.BaseStream,Console.OpenStandardError())); - output.Start(); error.Start(); - p.WaitForExit(); output.Join(); error.Join(); return p.ExitCode; - } catch { Console.Error.WriteLine("Owned native launcher integrity/configuration failure."); return 78; } - } -} + var argv=new List{"-u",script,manifest}; argv.AddRange(args); + var quoted=new List(); foreach(var a in argv) quoted.Add(Quote(a)); + var p=Process.Start(new ProcessStartInfo(python,String.Join(" ",quoted.ToArray())) { UseShellExecute=false, CreateNoWindow=true, RedirectStandardInput=true, RedirectStandardOutput=true, RedirectStandardError=true }); + var input=new Thread(()=> { try { Pump(Console.OpenStandardInput(),p.StandardInput.BaseStream); p.StandardInput.Close(); } catch(IOException) {} }); + input.IsBackground=true; input.Start(); + var output=new Thread(()=>Pump(p.StandardOutput.BaseStream,Console.OpenStandardOutput())); + var error=new Thread(()=>Pump(p.StandardError.BaseStream,Console.OpenStandardError())); + output.Start(); error.Start(); + p.WaitForExit(); output.Join(); error.Join(); return p.ExitCode; + } catch { Console.Error.WriteLine("Owned native launcher integrity/configuration failure."); return 78; } + } +} diff --git a/apps/desktop/assets/codex-bridge/test_native_speech_mux.py b/apps/desktop/assets/codex-bridge/test_native_speech_mux.py index 97fd5d6..4c7bb11 100644 --- a/apps/desktop/assets/codex-bridge/test_native_speech_mux.py +++ b/apps/desktop/assets/codex-bridge/test_native_speech_mux.py @@ -355,4 +355,3 @@ def test_verified_original_launcher_fallback_actual_process(tmp_path): raise AssertionError('not an app-server invocation') assert muxmod.launch_original(executable,digest,['-c',code,str(marker),'app-server'],attach=forbidden) == 21 assert marker.read_text() == 'ok' - diff --git a/apps/desktop/assets/codex-bridge/test_native_watch_cleanup.py b/apps/desktop/assets/codex-bridge/test_native_watch_cleanup.py index 6f9bb87..95ddba5 100644 --- a/apps/desktop/assets/codex-bridge/test_native_watch_cleanup.py +++ b/apps/desktop/assets/codex-bridge/test_native_watch_cleanup.py @@ -113,4 +113,3 @@ def test_executable_flushes_interactive_stdin_before_eof(tmp_path): assert q.get(timeout=5).strip()==b'live-before-eof' finally: p.stdin.close();p.wait(timeout=5) -