using System.Diagnostics; using System.Text.Json; using System.Text.RegularExpressions; namespace Dada.Supervisor; internal static partial class ControlledExternalValidationLauncher { private static readonly HashSet AllowedModels = [ "gemini-3.1-flash-image-preview", "gemini-3-pro-image-preview", "gpt-image-2", ]; private static readonly HashSet ValueOptions = [ "--candidate-record", "--config-manifest", "--evidence-dir", "--max-real-calls", "--model", "--run-id", "--service", ]; private static readonly HashSet SwitchOptions = [ "--confirm-controlled-real", "--execute-controlled-real", ]; internal static async Task RunAsync(string[] args, ICredentialStore credentials, CancellationToken cancellationToken = default) { var validated = ValidateArguments(args); var script = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, "scripts", "validate-external.mjs")); if (!File.Exists(script)) throw new InvalidOperationException("external_validator_not_found"); var startInfo = new ProcessStartInfo("node") { WorkingDirectory = Environment.CurrentDirectory }; startInfo.ArgumentList.Add(script); foreach (var value in validated) startInfo.ArgumentList.Add(value); startInfo.ArgumentList.Add("--credential-stdin"); var result = await CredentialProcessLauncher.RunToCompletionAsync(startInfo, ChildRole.Worker, credentials, cancellationToken); if (result.SensitiveOutputDetected || !TrySelectSanitizedJson(result, out var output, out var useError)) { Console.Error.WriteLine("{\"code\":\"external_validator_output_invalid\",\"real_calls\":0,\"status\":\"failed\"}"); return 1; } if (useError) Console.Error.WriteLine(output); else Console.WriteLine(output); return result.ExitCode; } internal static string[] ValidateArguments(string[] args) { var values = new Dictionary(StringComparer.Ordinal); var switches = new HashSet(StringComparer.Ordinal); for (var index = 0; index < args.Length; index++) { var option = args[index]; if (SwitchOptions.Contains(option)) { if (!switches.Add(option)) throw new ArgumentException("external_validator_argument_duplicate"); continue; } if (!ValueOptions.Contains(option) || index + 1 >= args.Length || !values.TryAdd(option, args[++index])) { throw new ArgumentException("external_validator_argument_invalid"); } } if (values.GetValueOrDefault("--service") != "ai-gateway-service-id" || !AllowedModels.Contains(values.GetValueOrDefault("--model") ?? string.Empty) || !SafeRunId().IsMatch(values.GetValueOrDefault("--run-id") ?? string.Empty) || values.GetValueOrDefault("--max-real-calls") != "120" || !values.ContainsKey("--candidate-record") || !values.ContainsKey("--config-manifest") || !values.ContainsKey("--evidence-dir") || !switches.SetEquals(SwitchOptions)) { throw new ArgumentException("external_validator_argument_invalid"); } if (values.Values.Any(value => value.Length == 0 || value.IndexOfAny(['\r', '\n', '\0']) >= 0)) { throw new ArgumentException("external_validator_argument_invalid"); } return args.ToArray(); } private static bool TrySelectSanitizedJson(CredentialProcessResult result, out string output, out bool useError) { var stdout = result.StandardOutput.Trim(); var stderr = result.StandardError.Trim(); useError = stdout.Length == 0; output = useError ? stderr : stdout; if (output.Length == 0 || (stdout.Length > 0 && stderr.Length > 0)) return false; try { using var document = JsonDocument.Parse(output); return IsSanitized(document.RootElement); } catch (JsonException) { return false; } } private static bool IsSanitized(JsonElement element) { if (element.ValueKind == JsonValueKind.Object) { foreach (var property in element.EnumerateObject()) { if (ForbiddenKey().IsMatch(property.Name) || property.NameEquals("verified") || !IsSanitized(property.Value)) return false; } } else if (element.ValueKind == JsonValueKind.Array) { foreach (var item in element.EnumerateArray()) if (!IsSanitized(item)) return false; } else if (element.ValueKind == JsonValueKind.String) { var value = element.GetString() ?? string.Empty; if (WindowsUserPath().IsMatch(value) || BearerValue().IsMatch(value)) return false; } return true; } [GeneratedRegex("^[A-Za-z0-9][A-Za-z0-9._-]{0,119}$", RegexOptions.CultureInvariant)] private static partial Regex SafeRunId(); [GeneratedRegex("(?:^|_)(?:absolute_path|authorization|body|credential|image|password|path|prompt|raw|secret|token)(?:_|$)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] private static partial Regex ForbiddenKey(); [GeneratedRegex("[A-Za-z]:\\\\Users\\\\", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] private static partial Regex WindowsUserPath(); [GeneratedRegex("(?:Bearer\\s+|\\bsk-[A-Za-z0-9_-]{8,})", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)] private static partial Regex BearerValue(); }