Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 65 additions & 18 deletions sqlnexus/ScriptIntegrity.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,78 @@
using System;
using NexusInterfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Security.Cryptography;

namespace sqlnexus
{
public static class ScriptIntegrityChecker
{

public static ILogger Logger { get; set; }

// Central logging helper – falls back to Trace / Console if no logger yet
private static void Log(string msg, bool verbose = false)
{
// Use local Logger if set, else Util.Logger
var lg = Logger ?? Util.Logger;
if (lg != null)
{
// Use Silent so it goes only to file (not status bar / dialogs)
lg.LogMessage("[ScriptIntegrity] " + msg, verbose ? MessageOptions.Silent : MessageOptions.Silent);
}
else
{
System.Diagnostics.Trace.WriteLine(msg);
}
}

// Store expected hashes for each allowed script

private static readonly Dictionary<string, string> ScriptHashes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
{ "PerfStatsAnalysis.sql", "37111E6F2052A2A7B15E26329475738B4E543FE66AFB7BA426639C062D9E81A1" },
{ "ReadTracePostProcessing.sql", "770DE7883BEFFA30C81C5BF45433EFF4C121EF92796047C49AC459103517BB68" },
{ "ReadTraceReportValidate.sql", "92A575503905D2CABEE18D1804D1DCDCACD12FACD912B16E1040C923AB168E02" },
{ "SQLNexus_PostProcessing.sql", "D6B0081152C262BFF6CBD0F04FC01B990E619DDD10FEDB8470438D0514E815FA" },
{ "SQLNexus_PreProcessing.sql", "81465871D11C26E93329C5F60CBACED1311E97205B29CD8E5526273018168FF6" },
{ "PostBuild.cmd", "741ABE8E8750EE4F010268B29C08B645EAB3EAE4E805D46CD5CA100926E00A48" },
{ "PostProcess.cmd", "814ABCA4622978440172394F24AD6C81535D0C78E53D4356642704A95CD38C7F" },
{ "PreBuild.cmd", "9C706DD338C5A3743C176E43F2C35FE765CF4719FBF33AF6FDAA811418B01187" },
};
{ Application.StartupPath + "\\" + "PerfStatsAnalysis.sql", "37111E6F2052A2A7B15E26329475738B4E543FE66AFB7BA426639C062D9E81A1" },
{ Application.StartupPath + "\\" + "ReadTracePostProcessing.sql", "770DE7883BEFFA30C81C5BF45433EFF4C121EF92796047C49AC459103517BB68" },
{ Application.StartupPath + "\\" + "ReadTraceReportValidate.sql", "92A575503905D2CABEE18D1804D1DCDCACD12FACD912B16E1040C923AB168E02" },
{ Application.StartupPath + "\\" + "SQLNexus_PostProcessing.sql", "BA659CE90DD602AD16C5A8F131D95C1A7D86AA00D764C68C3DE176C5AD0A4139" },
{ Application.StartupPath + "\\" + "SQLNexus_PreProcessing.sql", "81465871D11C26E93329C5F60CBACED1311E97205B29CD8E5526273018168FF6" },
{ Application.StartupPath + "\\" + "PostBuild.cmd", "741ABE8E8750EE4F010268B29C08B645EAB3EAE4E805D46CD5CA100926E00A48" },
{ Application.StartupPath + "\\" + "PostProcess.cmd", "814ABCA4622978440172394F24AD6C81535D0C78E53D4356642704A95CD38C7F" },
{ Application.StartupPath + "\\" + "PreBuild.cmd", "9C706DD338C5A3743C176E43F2C35FE765CF4719FBF33AF6FDAA811418B01187" }
};


// Returns true only if the file is listed and the hash matches
public static bool VerifyScript(string filePath)
{
string fileName = Path.GetFileName(filePath);
if (!ScriptHashes.ContainsKey(fileName))

Log($"Computing hash for file: {filePath}");

// Check if file is in the allowed list and get expected hash
if (!ScriptHashes.TryGetValue(filePath, out string expectedHash))
{
Log($"Script '{filePath}' not in the allowed list. Blocked.");
return false;
}

string expectedHash = ScriptHashes[fileName];
// Compute actual hash
string actualHash = ComputeFileHash(filePath);

return string.Equals(expectedHash, actualHash, StringComparison.OrdinalIgnoreCase);

if (actualHash == null)
{
Log($"Failed to compute hash for '{filePath}'.");
return false;
}

if (!string.Equals(expectedHash, actualHash, StringComparison.OrdinalIgnoreCase))
{
Log($"Hash mismatch for '{filePath}'. Expected={expectedHash} Actual={actualHash}");
return false;
}

Log($"Script '{filePath}' integrity OK.");
return true;
}

// Returns true only if the file is listed in the dictionary
Expand All @@ -44,15 +87,19 @@
try
{
using (var stream = File.OpenRead(filePath))
using (var sha = SHA256.Create())
{
byte[] hash = sha.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "");
using (var sha = SHA256.Create())
{
byte[] hash = sha.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "");
}
}

}
catch (Exception)
catch (Exception ex)
{
Log($"Error computing hash for '{filePath}': {ex.Message}");
return null;
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
}
}
Expand Down
23 changes: 16 additions & 7 deletions sqlnexus/fmImport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,10 @@ private void RunPostProcessing(string sourcePath)
psi.Arguments = string.Format("\"{0}\" \"{1}\" \"{2}\"", Globals.credentialMgr.Server, Globals.credentialMgr.Database, sourcePath);

MainForm.LogMessage("Executing: PostProcess.cmd " + psi.Arguments);
psi.FileName = "PostProcess.cmd";
psi.FileName = Application.StartupPath + "\\PostProcess.cmd";

//print psi filename full path
MainForm.LogMessage("PostProcess.cmd full path: " + Path.GetFullPath(psi.FileName));

// do a script validation before executing
if (!ScriptIntegrityChecker.VerifyScript(psi.FileName))
Expand Down Expand Up @@ -1370,15 +1373,10 @@ private void RunScript(string scriptname)
if (string.IsNullOrEmpty(scriptname))
return;

if (!ScriptIntegrityChecker.VerifyScript(scriptname))
{
MainForm.LogMessage("Script is not allowed or has been tampered with: '" + scriptname + "'", MessageOptions.All, TraceEventType.Error, "Script integrity");
return;
}


Cursor saveCur = this.Cursor;
string FullScriptName;

try
{
this.Cursor = Cursors.WaitCursor;
Expand All @@ -1394,6 +1392,7 @@ private void RunScript(string scriptname)
if (-1 == scriptname.IndexOf('\\'))
{
FullScriptName = Application.StartupPath + "\\" + scriptname;

if (!File.Exists(FullScriptName))
{
FullScriptName = Application.StartupPath + @"\Reports\" + scriptname;
Expand All @@ -1415,7 +1414,17 @@ private void RunScript(string scriptname)
{
MainForm.LogMessage("Script '" + FullScriptName + "' doesn't exist", MessageOptions.All);
return;
}

//print full path to the script
MainForm.LogMessage("Full path to script: " + Path.GetFullPath(FullScriptName));

// do a script validation before executing

if (!ScriptIntegrityChecker.VerifyScript(FullScriptName))
{
MainForm.LogMessage("Script is not allowed or has been tampered with: '" + scriptname + "'", MessageOptions.All, TraceEventType.Error, "Script integrity");
return;
}

//db.ExecuteNonQuery(File.ReadAllText(FullScriptName), Microsoft.SqlServer.Management.Common.ExecutionTypes.ContinueOnError);
Expand Down
Loading