Our organization utilizes VisualStudioOnline, GitHub and BitBucket for various repositories. I've been trying to figure out a way in c# to automate the pull of changes from various git repositories on a regular schedule. I've tried starting a process like this and then redirecting the standard input/output.
var p = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"C:\Program Files (x86)\Git\bin\sh.exe",
RedirectStandardInput = true,
UseShellExecute = false,
RedirectStandardOutput = true,
Arguments = "--login -i"
}
};
p.Start();
using (var so = p.StandardOutput)
{
using (var si = p.StandardInput)
{
if (si.BaseStream.CanWrite)
{
...
}
}
}
Inside of the block, I'm able to execute git commands like this:
si.WriteLine("cd \"P:/code/testapp\""); which navigates the git
shell to that directory
si.WriteLine("git rev-parse HEAD");
which will give me the latest hash of the repository at that location.
I can see the hash returned in the bash shell and can also read it in through the standard input like this var hash = so.ReadLine();
When I try to do si.WriteLint("git pull"); though, it asks for username and password, which I would expect, but that isn't done through the standard input/output. I can't us var prompt = so.ReadLine(); to get the prompt for username or password and I can't use si.WriteLine("username"); to input my password to the prompt.
I've tried a number of things to get this to work, but so far no luck.
WARNING: messy code ahead, just tried to quickly prototype something, not create a masterpiece
Attempt 1: used standard input/output redirects as shown above to try to write/read the prompts for username/password.
Attempt 2: attempt to get the ssh-agent process that is being utilized by the git bash and write to it
si.WriteLine("ssh-agent --help");
var sshAgentInfo = string.Empty;
var tmp = a.ReadLine();
while (a.Peek() != -1)
{
sshAgentInfo += tmp;
tmp = a.ReadLine();
}
var begPos = sshAgentInfo.IndexOf("SSH_AGENT_PID=", StringComparison.Ordinal);
begPos = begPos + "SSH_AGENT_PID=".Length;
var endPos = sshAgentInfo.IndexOf(";", begPos, StringComparison.Ordinal);
var processId = int.Parse(sshAgentInfo.Substring(begPos, endPos - begPos));
var sshProcess = Process.GetProcessById(processId);
sshProcess.StartInfo.RedirectStandardInput = true;
sshProcess.StartInfo.RedirectStandardInput = true;
using (var si1 = sshProcess.StandardInput) { ... }
Attempt 3: Utilize credential git config credential.helper wincred
This and four were very similar attempts. Again just trying to figure out how to set the password in either of these credential managers from the command line.
Attempt 4: Utilize Git-Credential-Manager-for-Windows
I've tried looking through the documentation here and it seems as though there is an enhancement request to do something along these lines.
Both of these seem to have similar problems to attempt one. The git shell seems to be invoking another process which handles the standard input and output separate from the git shell. The difference and hope I have for these last two though is, Is there a way to call into those credential managers directly to set username/passwords for different urls? In the .git directory, the config file has the credentials setting to tell it which manager to use and it stores the username and email in plain text.
If I invoke a git pull through the shell on the machine and enter my credentials once through that shell it seems to store them, but is there a way to allow users to enter their credentials through a website and then call into the manager through the command line to securely store that information for future automated use?
I also came across this. I haven't had a chance to try it yet, but is this a viable option for doing something like this?
Any help would be greatly appreciated.
Related
I am having a .Net Core 3.1 console application (which is work as a azure consumer for a message queue)
When executing the Consumer, based on a flag value of message property, Its need to run separate application which is written in node.js.
I decided use separate run.sh (shell script) file to call node application and run.sh file will call through c#.
Here is the code snippet I used.
var command = "bash";
var myBatchFile = #"C:\Temp\spiderease_v1\run.sh"; //Path to shell script file
var argss = #"C:\Temp\spiderease_v1\run.sh CA--STATCAN--REG--ADMIN-DATA {url} {OUTPUT_PATH} {SPIDER_EASE_HOME} {SPIDER_TEMPLATE_HOME} {TYPE}"; //this would become "/home/ubuntu/psa/PdfGeneration/ApacheFolder/ApacheFOP/transform.sh /home/ubuntu/psa/PdfGeneration/ApacheFolder/XMLFolder/test.xml /home/ubuntu/psa/PdfGeneration/ApacheFolder/XSLTFolder/Certificate.xsl /home/ubuntu/psa/PdfGeneration/ApacheFolder/PDFFolder/test.pdf"
var processInfo = new ProcessStartInfo();
processInfo.UseShellExecute = false;
processInfo.FileName = command; // 'sh' for bash
processInfo.Arguments = argss; // The Script name
processInfo.RedirectStandardOutput = true;
var process = Process.Start(processInfo); // Start that process.
var outPut = process.StandardOutput.ReadToEnd();
process.WaitForExit();
But This won't work as expected.
Update:
This code snipt didn't give any error, but I am unable to ensure that file getting executed or not. because when I run this bash file directly, it produced some files in given path. here it didn't give any output.
Can someone help me out whether do I using correct implementation for this task or do I have any alternatives?
If this is an good solution, what would be the wrong in code?
(I am willing push this into docker Linux environment as well)
Can someone help me out on this?
Answer might be a bit opinion-based but really important to me as I am pretty sure that certutil is accurate. Not sure for c# MD5 class.
I have a zip file and to verify if it's correct, I want to find its MD5 hash value. This is to then extract the zip file and use its contents in my C# .Net Framework 4.8 console application.
I have currently asked clients (each client has a my men appointed for tech support) to use CertUtil -hashfile command to get the hash and verify it but now, I guess due to increase in clients, I must automate it in my app and give a relief to my men.
I am confused should I use CertUtil and get the output in a C# string using Process.Start() or should i use the .net framework's MD5 class.
C# app is deployed only on windows 10 and I have administrative access to it so not finding certutil isn't an excuse.
Using CertUtil it will be something like this:
public static bool check_correct_installation()
{
var md5Checksum = "";
var startInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "cmd.exe",
Arguments = $"/C CertUtil -hashfile \"{HolocronFolders["Root"]}\" MD5 | find /i /v \"md5\" | find /i /v \"certutil\"",
};
using var process = new Process {StartInfo = startInfo};
process.OutputDataReceived += (sender, e) => md5Checksum = e.Data;
process.Start();
var fileToRead = $"{HolocronFolders["Council"]}\\force.sith";
if (!File.Exists(fileToRead)) return false;
var sithForce = JsonSerializer.Deserialize<SithForce>(File.ReadAllText(fileToRead));
return sithForce != null && sithForce.Checksum.Md5.ToString() != md5Checksum.Trim();
}
Yes, opinion based, but still here is mine:
Well, you're launching 4(!) processes for each signature you create (cmd.exe, certutil.exe and 2x find.exe). That alone would drag me away from it.
Then, the MD5 classes are being used in a multitude of applications projects, I would say there is no objective way to distrust them, unless you have a proven example where they were "wrong" or an security advisory, etc. that says so.
Finally, the MD5 implementation uses the underlying Windows API (see here to look into the rabbit whole) anyway. So chances are that it uses the same code (in the end) aus CertUtil.exe.
As you said, this might be an opinion-based answer, however I don't see many issues with sticking to your use as you stated with the code. Searching on the internet, it seems that other people chose similar approach, too. Give it a try!
I would like to execute a git diff command. The following command results in an 'fatal - path is outside repository' error. It is implemented in a C# application, using the Process class.
git diff HEAD -- "Folder\TestFile.cs" >
"C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff"
fatal: C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff:
C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff is outside repository.
The command 'works', because I could successfuly execute it in a command prompt (cmd).
Could you explain what is wrong with the command and how to solve it in the C# application?
It looks like you're trying to redirect standard output to a file. Redirection is handled by the shell, and the Process class does not support it. Instead, it's passing the > C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff to the git process. That's why git is complaining that the path C:\Users\Name\AppData\Local\Temp\tmpEA7C.diff is not in a repository.
If you're invoking a process and want to read its standard output, it's your responsibility to do that.
You can capture the output with the Process class. For example:
var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = "git.exe",
Arguments = "diff HEAD -- "Folder\TestFile.cs"",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
while (!proc.StandardOutput.EndOfStream) {
var line = proc.StandardOutput.ReadLine();
}
Of course, you could also use LibGit2Sharp and not have to worry about process creation and screen scraping the output.
On a Windows machine, is there a way to programmatically find out which application is responsible for opening files with a particular extension? Suppose I want to find out programmatically which application is responsible for opening .PDF files. (don't care if you have C# or VB.NET code)
Well, you will start out by looking in the registry in the following position:
HKEY_Current_User\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts.pdf\OpenWithList
There will be one or more keys from a and onwards, which point to the program used for opening a file of that type:
using Microsoft.Win32;
var key = Registry.CurrentUser
.OpenSubKey("Software")
.OpenSubKey("Microsoft")
.OpenSubKey("Windows")
.OpenSubKey("CurrentVersion")
.OpenSubKey("Explorer")
.OpenSubKey("FileExts")
.OpenSubKey(".doc")
.OpenSubKey("OpenWithList");
var firstProgram = key.GetValue("a"); // E.g. Winword.exe
You might want to split the assignment to key into several statements with null checks ;-)
Hope this helps!
The command-line command ASSOC finds file associations, and the command FTYPE finds actions assigned to them:
C:\> assoc .docx
.docx=Word.Document.12
C:\> ftype Word.Document.12
Word.Document.12="C:\Program Files (x86)\Microsoft Office\Office12\WINWORD.EXE" /n /dde
You can probably invoke them programmatically from any script.
From C#, you'd want to do something like this:
private string ShellCommand(string command)
{
var psi = new ProcessStartInfo("cmd", "/c " + command) {
RedirectStandardOutput = true,
CreateNoWindow = true
};
var p = Process.Start(psi);
return p.StandardOutput.ReadToEnd();
}
private string FindDefaultProgram(string extension)
{
assoc = ShellCommand("assoc " + extension).Split('=')[1];
program = ShellCommand("ftype " + assoc).Split('=')[1];
return program;
}
Haven't tested any of this, so take it with a grain of salt, but this should get you on the right track.
I won’t give you code but rather tell you where this information is stored – I’m sure you can figure out the rest on your own :)
So, all that data is stored inside the registry, in HKEY_CLASSES_ROOT. Taking .pdf as an example, there is a key .pdf which contains AcroExch.Document as it’s default value (on my setup at least).
Again in HKEY_CLASSES_ROOT there is a key AcroExch.Document\Shell\Open\Command and that one contains "C:\Program Files (x86)\Adobe\Acrobat 8.0\Acrobat\Acrobat.exe" "%1" as its value. And that’s what is being used on my computer to open a PDF file.
The action I need help about, is to execute a EXE file on own servers disk from a intranet-webpage, which IIS are on same server-installation. The webpage use a business layer to execute a ProcessStart together with given parameters.
When I perform the execution from web, the taskmanager show me that the application are starting up with the IIS AppPool of webpage as user. Few seconds later it's killed. In my database logs, I can see;
The Microsoft Jet database engine cannot open the file '\\computer\pathfile.ext'. It is already opened exclusively by another user, or you need permission to view its data.
That's correct. The EXE tool are, in turn, loading files from other computers. This is a special behavior which are well studied and well working while using the tool from desktop.
My goal/question,
I want this web-function-call behave with desktop rights. Is it possible at all?
The IIS AppPool have a regular setup with account ApplicationPoolIdentity. I appeared to be "lucky unwise", without knowledge about how much IIS 7.5 and Windows Server 2008 R2 raised the security model since <=IIS6.
I tried to change the app-pool user to NetworkService, Administrator.
I tried to set the application with app-pool as exec/read right
I even tried to let webapp to run a batch-file with a call to application inside..
Then I was begin to change the ProcessStart-behavior. And here, I
don't know much of what to do. I tried to add VERB runas. Force a
password prompt is not a solution here. I tried to simulate a
username/password. No luck there. I also tried to add runas /user:
blabla as parameters with ProcessStart, after used /savecred in a
desktop command window once. No luck there.
Maybe this should work but I just don't understand the correct setup of properties. I add the ProcessStart code snippet below, also added some commented code to let you see what I tried.
public string RunProcess(ApplicationType type, int param)
{
currentSelection = GetApplicationType(type);
ProcessStartInfo info = new ProcessStartInfo(currentSelection.Path);
info.CreateNoWindow = false;
info.UseShellExecute = true;
//info.UseShellExecute = false;
//info.ErrorDialog = false;
//info.UserName = "dummyUsEr";
//info.Password = this.SecurePwd("DummyPWd");
info.WindowStyle = ProcessWindowStyle.Normal;
info.Arguments = string.Format(" {0}", param.ToString());
using (Process exec = Process.Start(info))
{
try
{
exec.WaitForExit();
}
catch
{
}
}
return output;
}
EDIT
Just to be clear, and perhaps help some another guy/girl browsing to this question, I attach the snippet of Password-generation,
protected System.Security.SecureString SecurePwd(string pwd)
{
SecureString securePwd = new SecureString();
foreach (char ch in pwd.ToCharArray())
securePwd.AppendChar(ch);
return securePwd;
}
I see that you've tried putting in a specific username and password for the process start impersonation, but you say that the process accesses files on another computer and I don't see any mention of specifying a domain name which presumably you would need to access remote files?
So like this:
info.Domain = "domainname";
info.UserName = "dummyUsEr";
info.Password = "DummyPWd";
Also, what does this.SecurePwd() do and have you tried it with just the straight password string that you're passing into it?