mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-12-05 13:54:09 +00:00
Added the assembly loader with Azure Blob Storage
Added the assembly loader with Azure Blob Storage
This commit is contained in:
parent
e84a69d929
commit
07e47338cb
368
WelsonJS.Toolkit/WelsonJS.Launcher/AssemblyLoader.cs
Normal file
368
WelsonJS.Toolkit/WelsonJS.Launcher/AssemblyLoader.cs
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
// AssemblyLoader.cs
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2025 Catswords OSS and WelsonJS Contributors
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Network-aware loader for managed (.NET) and native (C/C++) binaries.
|
||||
/// - Managed assemblies resolve via AssemblyResolve
|
||||
/// - Native modules explicitly loaded via LoadNativeModules(...)
|
||||
/// - All DLLs must have valid Authenticode signatures
|
||||
/// - Cached at: %APPDATA%\WelsonJS\assembly\{Name}\{Version}\
|
||||
/// - BaseUrl must be set by Main() before calling Register()
|
||||
/// </summary>
|
||||
public static class AssemblyLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Base URL for downloading managed/native binaries.
|
||||
/// Example: https://catswords.blob.core.windows.net/welsonjs/packages
|
||||
/// Must be set before Register() or LoadNativeModules().
|
||||
/// </summary>
|
||||
public static string BaseUrl { get; set; } = null;
|
||||
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static bool _registered;
|
||||
|
||||
private static readonly string LoaderNamespace = typeof(AssemblyLoader).Namespace ?? "WelsonJS.Launcher";
|
||||
private static readonly HttpClient Http = new HttpClient();
|
||||
private static readonly ICompatibleLogger Logger = new TraceLogger();
|
||||
|
||||
// -------------------- kernel32 native loading --------------------
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool SetDllDirectory(string lpPathName);
|
||||
|
||||
// -------------------- WinVerifyTrust (signature verification) --------------------
|
||||
|
||||
private const uint ERROR_SUCCESS = 0x00000000;
|
||||
private const uint TRUST_E_NOSIGNATURE = 0x800B0100;
|
||||
private const uint TRUST_E_EXPLICIT_DISTRUST = 0x800B0111;
|
||||
private const uint TRUST_E_SUBJECT_NOT_TRUSTED = 0x800B0004;
|
||||
private const uint CRYPT_E_SECURITY_SETTINGS = 0x80092026;
|
||||
|
||||
private static readonly Guid WINTRUST_ACTION =
|
||||
new Guid("00aac56b-cd44-11d0-8cc2-00c04fc295ee");
|
||||
|
||||
[DllImport("wintrust.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern uint WinVerifyTrust(
|
||||
IntPtr hwnd,
|
||||
[MarshalAs(UnmanagedType.LPStruct)] Guid pgActionID,
|
||||
ref WINTRUST_DATA pWVTData);
|
||||
|
||||
|
||||
private enum FileSignatureStatus { Valid, NoSignature, Invalid }
|
||||
|
||||
|
||||
// -------------------- WinTrust Structures --------------------
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct WINTRUST_FILE_INFO
|
||||
{
|
||||
public uint cbStruct;
|
||||
[MarshalAs(UnmanagedType.LPWStr)] public string pcwszFilePath;
|
||||
public IntPtr hFile;
|
||||
public IntPtr pgKnownSubject;
|
||||
|
||||
public WINTRUST_FILE_INFO(string filePath)
|
||||
{
|
||||
cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO));
|
||||
pcwszFilePath = filePath;
|
||||
hFile = IntPtr.Zero;
|
||||
pgKnownSubject = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private enum WinTrustDataUIChoice : uint { None = 2 }
|
||||
private enum WinTrustDataRevocationChecks : uint { None = 0 }
|
||||
private enum WinTrustDataChoice : uint { File = 1 }
|
||||
private enum WinTrustDataStateAction : uint { Ignore = 0 }
|
||||
private enum WinTrustDataUIContext : uint { Execute = 0 }
|
||||
[Flags]
|
||||
private enum WinTrustDataProvFlags : uint
|
||||
{
|
||||
RevocationCheckNone = 0x00000010,
|
||||
DisableMD2andMD4 = 0x00002000
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct WINTRUST_DATA
|
||||
{
|
||||
public uint cbStruct;
|
||||
public IntPtr pPolicyCallbackData;
|
||||
public IntPtr pSIPClientData;
|
||||
public WinTrustDataUIChoice dwUIChoice;
|
||||
public WinTrustDataRevocationChecks dwRevocationChecks;
|
||||
public WinTrustDataChoice dwUnionChoice;
|
||||
public IntPtr pFile;
|
||||
public WinTrustDataStateAction dwStateAction;
|
||||
public IntPtr hWVTStateData;
|
||||
public string pwszURLReference;
|
||||
public WinTrustDataProvFlags dwProvFlags;
|
||||
public WinTrustDataUIContext dwUIContext;
|
||||
|
||||
public WINTRUST_DATA(IntPtr pFileInfo)
|
||||
{
|
||||
cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA));
|
||||
pPolicyCallbackData = IntPtr.Zero;
|
||||
pSIPClientData = IntPtr.Zero;
|
||||
dwUIChoice = WinTrustDataUIChoice.None;
|
||||
dwRevocationChecks = WinTrustDataRevocationChecks.None;
|
||||
dwUnionChoice = WinTrustDataChoice.File;
|
||||
pFile = pFileInfo;
|
||||
dwStateAction = WinTrustDataStateAction.Ignore;
|
||||
hWVTStateData = IntPtr.Zero;
|
||||
pwszURLReference = null;
|
||||
dwProvFlags = WinTrustDataProvFlags.RevocationCheckNone |
|
||||
WinTrustDataProvFlags.DisableMD2andMD4;
|
||||
dwUIContext = WinTrustDataUIContext.Execute;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// PUBLIC API
|
||||
// ========================================================================
|
||||
|
||||
/// <summary>
|
||||
/// Registers AssemblyResolve to download and validate .NET assemblies.
|
||||
/// </summary>
|
||||
public static void Register()
|
||||
{
|
||||
if (_registered)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(BaseUrl))
|
||||
{
|
||||
Logger.Error("AssemblyLoader.Register() called but BaseUrl is not set.");
|
||||
throw new InvalidOperationException("AssemblyLoader.BaseUrl must be configured before Register().");
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
|
||||
_registered = true;
|
||||
|
||||
Logger.Info("AssemblyLoader: AssemblyResolve handler registered.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Loads native modules associated with an assembly (explicit).
|
||||
/// </summary>
|
||||
public static void LoadNativeModules(string ownerAssemblyName, Version version, IList<string> fileNames)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(BaseUrl))
|
||||
throw new InvalidOperationException("AssemblyLoader.BaseUrl must be set before loading native modules.");
|
||||
|
||||
if (ownerAssemblyName == null) throw new ArgumentNullException("ownerAssemblyName");
|
||||
if (version == null) throw new ArgumentNullException("version");
|
||||
if (fileNames == null) throw new ArgumentNullException("fileNames");
|
||||
|
||||
string versionString = version.ToString();
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string cacheDir = Path.Combine(appData, "WelsonJS", "assembly", ownerAssemblyName, versionString);
|
||||
Directory.CreateDirectory(cacheDir);
|
||||
|
||||
try { SetDllDirectory(cacheDir); }
|
||||
catch { }
|
||||
|
||||
foreach (string raw in fileNames)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
continue;
|
||||
|
||||
string fileName = raw.Trim();
|
||||
string localPath = Path.Combine(cacheDir, fileName);
|
||||
|
||||
if (!File.Exists(localPath))
|
||||
{
|
||||
string url = $"{BaseUrl.TrimEnd('/')}/native/{ownerAssemblyName}/{versionString}/{fileName}";
|
||||
DownloadFile(url, localPath);
|
||||
Logger.Info("Downloaded native module: {0}", fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Using cached native module: {0}", localPath);
|
||||
}
|
||||
|
||||
EnsureSignedFileOrThrow(localPath, fileName);
|
||||
|
||||
IntPtr h = LoadLibrary(localPath);
|
||||
if (h == IntPtr.Zero)
|
||||
{
|
||||
Logger.Error("LoadLibrary failed for {0}", localPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Loaded native module: {0}", fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void LoadNativeModules(Assembly asm, IList<string> fileNames)
|
||||
{
|
||||
AssemblyName an = asm.GetName();
|
||||
LoadNativeModules(an.Name, an.Version, fileNames);
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// ASSEMBLY RESOLVE HANDLER (MANAGED)
|
||||
// ========================================================================
|
||||
|
||||
private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
Logger.Info("AssemblyResolve: {0}", args.Name);
|
||||
|
||||
AssemblyName req = new AssemblyName(args.Name);
|
||||
string simpleName = req.Name;
|
||||
if (IsFrameworkAssembly(simpleName))
|
||||
return null;
|
||||
|
||||
var entry = Assembly.GetEntryAssembly();
|
||||
if (entry != null)
|
||||
{
|
||||
var entryName = entry.GetName().Name;
|
||||
if (string.Equals(simpleName, entryName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Logger.Info("AssemblyResolve: skipping entry assembly {0}", simpleName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Version version = req.Version ?? new Version(0, 0, 0, 0);
|
||||
string versionStr = version.ToString();
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string cacheDir = Path.Combine(appData, "WelsonJS", "assembly", simpleName, versionStr);
|
||||
string dllPath = Path.Combine(cacheDir, simpleName + ".dll");
|
||||
|
||||
Directory.CreateDirectory(cacheDir);
|
||||
|
||||
if (!File.Exists(dllPath))
|
||||
{
|
||||
string url = $"{BaseUrl.TrimEnd('/')}/managed/{simpleName}/{versionStr}/{simpleName}.dll";
|
||||
DownloadFile(url, dllPath);
|
||||
Logger.Info("Downloaded managed assembly: {0}", simpleName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Using cached managed assembly: {0}", dllPath);
|
||||
}
|
||||
|
||||
if (!File.Exists(dllPath))
|
||||
{
|
||||
Logger.Warn("AssemblyResolve: managed assembly not found after download attempt: {0}", simpleName);
|
||||
return null;
|
||||
}
|
||||
|
||||
EnsureSignedFileOrThrow(dllPath, simpleName);
|
||||
return Assembly.LoadFrom(dllPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// HELPERS
|
||||
// ========================================================================
|
||||
|
||||
private static void DownloadFile(string url, string dest)
|
||||
{
|
||||
HttpResponseMessage res = null;
|
||||
|
||||
try
|
||||
{
|
||||
res = Http.GetAsync(url).GetAwaiter().GetResult();
|
||||
if (res.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
Logger.Warn("DownloadFile: 404 Not Found for {0}", url);
|
||||
return;
|
||||
}
|
||||
|
||||
res.EnsureSuccessStatusCode();
|
||||
|
||||
using (Stream s = res.Content.ReadAsStreamAsync().GetAwaiter().GetResult())
|
||||
using (FileStream fs = new FileStream(dest, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
s.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Logger.Error("DownloadFile: HTTP error for {0}: {1}", url, ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static bool IsFrameworkAssembly(string name)
|
||||
{
|
||||
return name.StartsWith("System", StringComparison.OrdinalIgnoreCase) ||
|
||||
name.StartsWith("Microsoft", StringComparison.OrdinalIgnoreCase) ||
|
||||
name == "mscorlib" ||
|
||||
name == "netstandard" ||
|
||||
name == "WindowsBase" ||
|
||||
name == "PresentationCore" ||
|
||||
name == "PresentationFramework" ||
|
||||
name.StartsWith(LoaderNamespace);
|
||||
}
|
||||
|
||||
|
||||
private static void EnsureSignedFileOrThrow(string path, string logicalName)
|
||||
{
|
||||
FileSignatureStatus status = VerifySignature(path);
|
||||
|
||||
if (status == FileSignatureStatus.Valid)
|
||||
{
|
||||
Logger.Info("Signature OK: {0}", logicalName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == FileSignatureStatus.NoSignature)
|
||||
{
|
||||
Logger.Error("BLOCKED unsigned binary: {0}", logicalName);
|
||||
throw new InvalidOperationException("Unsigned binary blocked: " + logicalName);
|
||||
}
|
||||
|
||||
Logger.Error("BLOCKED invalid signature: {0}", logicalName);
|
||||
throw new InvalidOperationException("Invalid signature: " + logicalName);
|
||||
}
|
||||
|
||||
|
||||
private static FileSignatureStatus VerifySignature(string file)
|
||||
{
|
||||
WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(file);
|
||||
IntPtr pFile = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)));
|
||||
|
||||
Marshal.StructureToPtr(fileInfo, pFile, false);
|
||||
WINTRUST_DATA data = new WINTRUST_DATA(pFile);
|
||||
|
||||
uint result = WinVerifyTrust(IntPtr.Zero, WINTRUST_ACTION, ref data);
|
||||
|
||||
Marshal.FreeCoTaskMem(pFile);
|
||||
|
||||
if (result == ERROR_SUCCESS) return FileSignatureStatus.Valid;
|
||||
if (result == TRUST_E_NOSIGNATURE) return FileSignatureStatus.NoSignature;
|
||||
|
||||
return FileSignatureStatus.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,244 +0,0 @@
|
|||
// NativeBootstrap.cs
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2025 Catswords OSS and WelsonJS Contributors
|
||||
// https://github.com/gnh1201/welsonjs
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WelsonJS.Launcher
|
||||
{
|
||||
public static class NativeBootstrap
|
||||
{
|
||||
// Win32 APIs
|
||||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true)]
|
||||
private static extern bool SetDefaultDllDirectories(int DirectoryFlags);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "AddDllDirectory")]
|
||||
private static extern IntPtr AddDllDirectory(string newDirectory);
|
||||
|
||||
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool SetDllDirectory(string lpPathName);
|
||||
|
||||
private const int LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to load native libraries in the following order:
|
||||
/// 1) %APPDATA%\{appDataSubdirectory}\{dllName}
|
||||
/// 2) Application base directory\{dllName}
|
||||
///
|
||||
/// Signatures:
|
||||
/// - requireSigned = true : Only loads DLLs with a valid Authenticode chain.
|
||||
/// - certValidator != null : Additional custom validation (e.g., pinning).
|
||||
///
|
||||
/// Must be called before any P/Invoke usage.
|
||||
/// </summary>
|
||||
public static void Init(
|
||||
IEnumerable<string> dllNames,
|
||||
string appDataSubdirectory,
|
||||
ICompatibleLogger logger,
|
||||
bool requireSigned = false,
|
||||
Func<X509Certificate2, bool> certValidator = null)
|
||||
{
|
||||
if (dllNames == null) throw new ArgumentNullException(nameof(dllNames));
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
string appDataPath = string.IsNullOrEmpty(appDataSubdirectory)
|
||||
? appData
|
||||
: Path.Combine(appData, appDataSubdirectory);
|
||||
|
||||
string asmLocation = Assembly.GetEntryAssembly()?.Location
|
||||
?? Assembly.GetExecutingAssembly().Location
|
||||
?? AppContext.BaseDirectory;
|
||||
string appBaseDirectory = Path.GetDirectoryName(asmLocation) ?? AppContext.BaseDirectory;
|
||||
|
||||
var triedPaths = new List<string>();
|
||||
|
||||
foreach (string dllName in dllNames)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dllName))
|
||||
continue;
|
||||
|
||||
// 1) %APPDATA% subdirectory
|
||||
string candidate1 = Path.Combine(appDataPath, dllName);
|
||||
triedPaths.Add(candidate1);
|
||||
if (TryLoad(candidate1, logger, requireSigned, certValidator)) return;
|
||||
|
||||
// 2) Application base directory
|
||||
string candidate2 = Path.Combine(appBaseDirectory, dllName);
|
||||
triedPaths.Add(candidate2);
|
||||
if (TryLoad(candidate2, logger, requireSigned, certValidator)) return;
|
||||
}
|
||||
|
||||
string message = "Failed to load requested native libraries.\n" +
|
||||
"Tried:\n " + string.Join("\n ", triedPaths);
|
||||
logger.Error(message);
|
||||
throw new FileNotFoundException(message);
|
||||
}
|
||||
|
||||
private static bool TryLoad(
|
||||
string fullPath,
|
||||
ICompatibleLogger logger,
|
||||
bool requireSigned,
|
||||
Func<X509Certificate2, bool> certValidator)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
logger.Info($"Not found: {fullPath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional signature validation
|
||||
if (!ValidateSignatureIfRequired(fullPath, requireSigned, certValidator, logger))
|
||||
{
|
||||
// If requireSigned=false we never reach here (it would return true).
|
||||
logger.Warn($"Signature validation failed: {fullPath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
string directoryPath = Path.GetDirectoryName(fullPath) ?? AppContext.BaseDirectory;
|
||||
if (!TryRegisterSearchDirectory(directoryPath, logger))
|
||||
{
|
||||
logger.Warn($"Could not register search directory: {directoryPath}");
|
||||
}
|
||||
|
||||
logger.Info($"Loading: {fullPath}");
|
||||
IntPtr handle = LoadLibrary(fullPath);
|
||||
if (handle == IntPtr.Zero)
|
||||
{
|
||||
int err = Marshal.GetLastWin32Error();
|
||||
logger.Warn($"LoadLibrary failed for {fullPath} (Win32Error={err}).");
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.Info($"Successfully loaded: {fullPath}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Warn($"Exception while loading {fullPath}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If requireSigned=false, returns true (no check).
|
||||
/// If requireSigned=true, verifies Authenticode chain and optional custom validator.
|
||||
/// </summary>
|
||||
private static bool ValidateSignatureIfRequired(
|
||||
string path,
|
||||
bool requireSigned,
|
||||
Func<X509Certificate2, bool> certValidator,
|
||||
ICompatibleLogger logger)
|
||||
{
|
||||
if (!requireSigned)
|
||||
{
|
||||
// No signature requirement: allow loading regardless of signature.
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Throws on unsigned files.
|
||||
var baseCert = X509Certificate.CreateFromSignedFile(path);
|
||||
if (baseCert == null)
|
||||
{
|
||||
logger.Warn("No certificate extracted from file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var cert = new X509Certificate2(baseCert);
|
||||
|
||||
var chain = new X509Chain
|
||||
{
|
||||
ChainPolicy =
|
||||
{
|
||||
RevocationMode = X509RevocationMode.Online,
|
||||
RevocationFlag = X509RevocationFlag.ExcludeRoot,
|
||||
VerificationFlags = X509VerificationFlags.NoFlag,
|
||||
VerificationTime = DateTime.UtcNow
|
||||
}
|
||||
};
|
||||
|
||||
bool chainOk = chain.Build(cert);
|
||||
if (!chainOk)
|
||||
{
|
||||
foreach (var status in chain.ChainStatus)
|
||||
logger.Warn($"Cert chain status: {status.Status} - {status.StatusInformation?.Trim()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Optional extra validation, e.g. thumbprint or subject pinning.
|
||||
if (certValidator != null)
|
||||
{
|
||||
bool ok = false;
|
||||
try { ok = certValidator(cert); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Warn($"Custom certificate validator threw: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
logger.Warn("Custom certificate validator rejected the certificate.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info($"Signature validated. Subject='{cert.Subject}', Thumbprint={cert.Thumbprint}");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Warn($"Signature check failed: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryRegisterSearchDirectory(string directoryPath, ICompatibleLogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool ok = SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
||||
if (!ok)
|
||||
{
|
||||
int e = Marshal.GetLastWin32Error();
|
||||
logger.Warn($"SetDefaultDllDirectories failed (Win32Error={e}), fallback to SetDllDirectory.");
|
||||
return SetDllDirectory(directoryPath);
|
||||
}
|
||||
|
||||
IntPtr cookie = AddDllDirectory(directoryPath);
|
||||
if (cookie == IntPtr.Zero)
|
||||
{
|
||||
int e = Marshal.GetLastWin32Error();
|
||||
logger.Warn($"AddDllDirectory failed (Win32Error={e}), fallback to SetDllDirectory.");
|
||||
return SetDllDirectory(directoryPath);
|
||||
}
|
||||
|
||||
logger.Info($"Registered native DLL search directory: {directoryPath}");
|
||||
return true;
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
logger.Warn("DefaultDllDirectories API not available. Using SetDllDirectory fallback.");
|
||||
return SetDllDirectory(directoryPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Warn($"Register search directory failed for {directoryPath}: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,19 +32,10 @@ namespace WelsonJS.Launcher
|
|||
// set up logger
|
||||
_logger = new TraceLogger();
|
||||
|
||||
// load native libraries
|
||||
string appDataSubDirectory = "WelsonJS";
|
||||
bool requireSigned = string.Equals(
|
||||
GetAppConfig("NativeRequireSigned"),
|
||||
"true",
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
NativeBootstrap.Init(
|
||||
dllNames: new[] { "ChakraCore.dll" },
|
||||
appDataSubdirectory: appDataSubDirectory,
|
||||
logger: _logger,
|
||||
requireSigned: requireSigned
|
||||
);
|
||||
// load external assemblies
|
||||
AssemblyLoader.BaseUrl = GetAppConfig("AssemblyBaseUrl");
|
||||
AssemblyLoader.Register();
|
||||
AssemblyLoader.LoadNativeModules("ChakraCore", new Version(1, 13, 0, 0), new[] { "ChakraCore.dll" });
|
||||
|
||||
// telemetry
|
||||
try
|
||||
|
|
|
|||
|
|
@ -60,6 +60,15 @@ namespace WelsonJS.Launcher.Properties {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://catswords.blob.core.windows.net/welsonjs/packages과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
internal static string AssemblyBaseUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("AssemblyBaseUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 과(와) 유사한 지역화된 문자열을 찾습니다.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -241,4 +241,7 @@
|
|||
<data name="TelemetryEnabled" xml:space="preserve">
|
||||
<value>true</value>
|
||||
</data>
|
||||
<data name="AssemblyBaseUrl" xml:space="preserve">
|
||||
<value>https://catswords.blob.core.windows.net/welsonjs/packages</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -88,12 +88,12 @@
|
|||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyLoader.cs" />
|
||||
<Compile Include="ICompatibleLogger.cs" />
|
||||
<Compile Include="IResourceTool.cs" />
|
||||
<Compile Include="JsCore.cs" />
|
||||
<Compile Include="JsNative.cs" />
|
||||
<Compile Include="JsSerializer.cs" />
|
||||
<Compile Include="NativeBootstrap.cs" />
|
||||
<Compile Include="ResourceTools\ImageColorPicker.cs" />
|
||||
<Compile Include="ResourceTools\IpQuery.cs" />
|
||||
<Compile Include="ResourceTools\Settings.cs" />
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
<add key="TelemetryApiKey" value="phc_pmRHJ0aVEhtULRT4ilexwCjYpGtE9VYRhlA05fwiYt8"/>
|
||||
<add key="TelemetryBaseUrl" value="https://us.i.posthog.com"/>
|
||||
<add key="TelemetryEnabled" value="true"/>
|
||||
<add key="AssemblyBaseUrl" value="https://catswords.blob.core.windows.net/welsonjs/packages"/>
|
||||
</appSettings>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user